├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierrc.js ├── LICENSE ├── README.md ├── SECURITY.md ├── bundle.ts ├── code-of-conduct.md ├── contributing.md ├── deno.json ├── docs ├── assets │ ├── Request-Lifecycle.svg │ ├── custom-typography.css │ ├── favicon.ico │ ├── home.png │ ├── icon-transparent.webp │ ├── icon.webp │ ├── index.css │ ├── index.html │ ├── prism.css │ └── prism.js ├── discord │ └── index.html ├── docs │ ├── credits.md │ ├── docs │ │ ├── context.md │ │ ├── deployment.md │ │ ├── error-handling.md │ │ ├── installation.md │ │ ├── introduction.md │ │ ├── middleware.md │ │ ├── plugin.md │ │ ├── plugins.md │ │ ├── quickstart.md │ │ ├── request-lifecycle.md │ │ ├── resources │ │ │ └── resources.md │ │ └── routing.md │ └── home-links │ │ └── tutorial.md └── fragments │ ├── docs.html │ └── other.html ├── example ├── README.md ├── app.jet.ts ├── chat.html ├── data │ └── models.ts ├── definitions.ts ├── index.jet.ts ├── middleware │ └── global.ts ├── plugins │ ├── auth.ts │ └── logging.ts ├── routes │ ├── auth.jet.ts │ ├── live.jet.ts │ ├── pets.jet.ts │ ├── reviews.jet.ts │ └── utils.jet.ts ├── types.ts └── websockets-usage.md ├── icon-transparent.png ├── icon.png ├── pack ├── package-lock.json ├── package.json ├── repository-open-graph-template.png ├── src ├── assets │ ├── api-doc.html │ └── bundle.ts ├── cli.ts ├── extracts │ └── mimejs-extract.ts ├── index.ts └── primitives │ ├── classes.ts │ ├── functions.ts │ └── types.ts ├── tsconfig.d.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | docs/build 2 | example 3 | bundle.ts 4 | dist 5 | definitions.jet.ts -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/gts/" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | ./lib 3 | **.tgz 4 | jetpath-**.tgz 5 | jetpath-** 6 | .history 7 | api-doc.http 8 | dist 9 | ./dist 10 | .vscode 11 | official-plugins 12 | security-audits 13 | formdata-psrser.ts 14 | tests/uploads 15 | docmach 16 | ccokies.ts 17 | repository-open-graph-template.png 18 | repository-open-graph-template.xcf 19 | example/uploads 20 | example/petshop-api-log.log 21 | example/val.ts 22 | example/ws.jet.ts 23 | tbench.ts 24 | trie.ts 25 | audit-server 26 | apis-types.d.ts 27 | NEXT-FEATURES.md 28 | example/pet-shop-api-log.log 29 | src/assets/bundle.js 30 | bench.sh 31 | **.log 32 | docs/build 33 | definitions.jet.ts -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | export default { 2 | ...require('node_modules/gts/.prettierrc.json') 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | Copyright 2023 friday candour 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Jetpath 3 |

4 | 5 |

Jetpath

6 | 7 |

8 | A performance-first cross-runtime API framework without the boilerplate 9 |
10 |
11 | Documentation » 12 |
13 |
14 | Join Discord 15 | · 16 | Report Bug 17 | · 18 | Request Feature 19 |

20 | 21 |
22 | 23 | Downloads per Month 24 | 25 | 26 | Downloads per Year 27 | 28 | 29 | npm version 30 | 31 | 32 | Stars 33 | 34 |
35 | 36 | ## Why Engineers Choose Jetpath 37 | 38 | Every framework promises to be fast and easy. Jetpath got built because most of them aren't. 39 | 40 | ```ts 41 | // This is a complete API endpoint in Jetpath 42 | export const GET_users_$id: JetRoute = async function (ctx) { 43 | const { id } = ctx.params; 44 | const user = await db.users.findUnique({ where: { id } }); 45 | return ctx.send(user); 46 | }; 47 | ``` 48 | 49 | Jetpath eliminates the cognitive overhead that slows down development. No more router configuration, middleware chains, or callback hell. Just pure functions that map directly to HTTP endpoints through a clean, predictable naming convention. 50 | 51 | **The tech stack you already trust, but faster:** 52 | - Write APIs in TypeScript/JavaScript across Node.js, Deno, or Bun 53 | - ~50% less code than Express with stronger type safety 54 | - [Benchmarks](https://github.com/CodeDynasty-dev/jetpath-benchmark) show massive throughput compared to Elysia.js. 55 | 56 | ## Core Design Principles 57 | 58 | Jetpath is built with strong opinions on what matters most: 59 | 60 | 1. **Zero config by default** - Convention eliminates boilerplate 61 | 2. **Runtime agnostic** - True support for Node.js, Deno, and Bun (not just compatibility layers) 62 | 3. **Type safety** - Full TypeScript support that doesn't get in your way 63 | 4. **Predictable routing** - Routes derived from function names (GET_users_$id → GET /users/:id) 64 | 5. **Built for production** - Security, validation, and error handling baked in 65 | 66 | ## In Production 67 | 68 | I am using Jetpath in production and here are the results. 69 | - 40% reduction in API codebase size 70 | - Simplified onboarding for new team members 71 | - Faster iterations on API endpoints 72 | 73 | ## Quick Start 74 | 75 | ```bash 76 | # Create new project 77 | npx jetpath new-project 78 | 79 | # Navigate and start the dev server 80 | cd new-project && npm install && npm run dev 81 | ``` 82 | 83 | ## API Design That Gets Out of Your Way 84 | 85 | ```ts 86 | import { type JetRoute, Jetpath, use } from "jetpath"; 87 | 88 | const app = new Jetpath(); 89 | app.listen(3000); 90 | 91 | // GET /products 92 | export const GET_products: JetRoute = async (ctx) => { 93 | const products = await db.products.findMany(); 94 | ctx.send({ products }); 95 | }; 96 | 97 | // POST /products with validation 98 | export const POST_products: JetRoute = async (ctx) => { 99 | const data = await ctx.parse(); 100 | const product = await db.products.create({ data }); 101 | ctx.send({ product }, 201); 102 | }; 103 | 104 | // Add validation and docs in one step 105 | use(POST_products) 106 | .title("Create a new product") 107 | .body((t) => ({ 108 | name: t.string().required().min(3), 109 | price: t.number().required().min(0), 110 | description: t.string() 111 | })); 112 | 113 | // Maps to ws://your-host/live 114 | export const GET_live: JetRoute = (ctx) => { 115 | ctx.upgrade(); 116 | const conn = ctx.connection!; 117 | conn.addEventListener("open", (socket) => { /* ... */ }); 118 | conn.addEventListener("message", (socket, event) => { /* ... */ }); 119 | }; 120 | ``` 121 | 122 | ## Key Features 123 | 124 | - **Unified dev experience** across Node.js, Deno, and Bun 125 | - **Auto-generated API documentation** with interactive UI 126 | - **First-class WebSocket support** 127 | - **Plugin system** for extending functionality 128 | - **Schema validation** that is part of api documentation 129 | - **Request parsing** that just works (JSON, forms, multipart) 130 | - **Performance-optimized** routing and middleware execution 131 | - **Security** good defaults 132 | 133 | ## Real Performance 134 | 135 | It's not just a claim how fast - measure it. In the [benchmark suite](hhttps://github.com/CodeDynasty-dev/jetpath-benchmark), Jetpath consistently perform close to raw Bunjs performance matches elysia.js on common API workloads: 136 | 137 | | Framework | Requests/sec | Latency (avg) 138 | |-----------|-------------|---------------| 139 | | Bun | ~40,890 | 12.2ms | 140 | | Elysia | ~33,383 | 13.2ms | 141 | | Jetpath | ~32,339 | 13.7ms | 142 | 143 | *4-core CPU, 1000 concurrent connections and 1,000,000 requests, simple JSON response* 144 | 145 | Bunjs being amongst the fastest http runtime. 146 | 147 | ## Installation 148 | 149 | For existing projects: 150 | 151 | ```bash 152 | npm install jetpath --save 153 | ``` 154 | 155 | ## Community & Support 156 | 157 | - [Documentation](https://jetpath.codedynasty.dev) - In-depth guides and API reference 158 | - [Discord Community](https://discord.gg/faqydQASTy) - Get help from the team and other users 159 | - [GitHub Issues](https://github.com/codedynasty-dev/jetpath/issues) - Report bugs or request features 160 | 161 | ## License 162 | 163 | Apache 2.0 - Open source and built for the community. 164 | 165 | ### Contributing 166 | 167 | We welcome contributions! See our [contributing guide](https://github.com/CodeDynasty-dev/Jetpath/blob/main/contributing.md) for details on how to get involved. 168 | 169 | By contributing, you agree to license your code under the Apache 2.0 license and confirm that all contributions are your original work. 170 | 171 | ### Support or Sponsor the Project 172 | 173 | If Jetpath helps you or your team ship faster and more understandable codebase, consider supporting its development through [GitHub Sponsors](https://github.com/sponsors/CodeDynasty-dev). -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Reporting a Vulnerability 2 | 3 | Please report security vulnerabilities to fridaycandours@gmail.com. 4 | -------------------------------------------------------------------------------- /bundle.ts: -------------------------------------------------------------------------------- 1 | import { readFile, writeFile } from "fs/promises"; 2 | // ? This file does a lot of in just few lines thanks to bunjs 3 | console.log("Jetpath: compiling..."); 4 | const html = await readFile("src/assets/api-doc.html", { 5 | encoding: "utf-8", 6 | }); 7 | const code = await readFile("dist/index.js", { 8 | encoding: "utf-8", 9 | }); 10 | await Bun.build({ 11 | entrypoints: ["src/assets/bundle.ts"], 12 | outdir: "src/assets", 13 | minify: true, 14 | // additional config 15 | }); 16 | const html_script_code = await readFile("src/assets/bundle.js", { 17 | encoding: "utf-8", 18 | }); 19 | const view = html.replaceAll(/(\n|\r|\s{2,})/g, "").replace( 20 | "{JETPATH-DOC-SCRIPT}", 21 | html_script_code, 22 | ).replaceAll(/`/g, "\\`") 23 | .replaceAll(/\${/g, "\\${"); 24 | await writeFile("dist/index.js", code.replace("{{view}}", view)); 25 | console.log("Jetpath: compiled!"); 26 | 27 | // [X] npm pack will call npm run prepare which will run this file 28 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | As a member of the JetPath project, JetPath has adopted the 4 | [Contributor Covenant 2.0][cc-20-doc]. 5 | 6 | If an issue arises and you cannot resolve it directly with the parties 7 | involved, you can report it to the JetPath project technical committee through the following 8 | email: {} 9 | 10 | 11 | ## Our Pledge 12 | 13 | We as members, contributors, and leaders pledge to make participation in our 14 | community a harassment-free experience for everyone, regardless of age, body 15 | size, visible or invisible disability, ethnicity, sex characteristics, gender 16 | identity and expression, level of experience, education, socio-economic status, 17 | nationality, personal appearance, race, religion, or sexual identity and 18 | orientation. 19 | 20 | We pledge to act and interact in ways that contribute to an open, welcoming, 21 | diverse, inclusive, and healthy community. 22 | 23 | ## Our Standards 24 | 25 | Examples of behavior that contributes to a positive environment for our 26 | community include: 27 | 28 | * Demonstrating empathy and kindness toward other people 29 | * Being respectful of differing opinions, viewpoints, and experiences 30 | * Giving and gracefully accepting constructive feedback 31 | * Accepting responsibility and apologizing to those affected by our mistakes, 32 | and learning from the experience 33 | * Focusing on what is best not just for us as individuals, but for the overall 34 | community 35 | 36 | Examples of unacceptable behavior include: 37 | 38 | * The use of sexualized language or imagery, and sexual attention or advances 39 | of any kind 40 | * Trolling, insulting or derogatory comments, and personal or political attacks 41 | * Public or private harassment 42 | * Publishing others' private information, such as a physical or email address, 43 | without their explicit permission 44 | * Other conduct which could reasonably be considered inappropriate in a 45 | professional setting 46 | 47 | ## Enforcement Responsibilities 48 | 49 | The project team are responsible for clarifying and enforcing our standards of 50 | acceptable behavior and will take appropriate and fair corrective action in 51 | response to any behavior that they deem inappropriate, threatening, offensive, 52 | or harmful. 53 | 54 | The project team have the right and responsibility to remove, edit, or reject 55 | comments, commits, code, wiki edits, issues, and other contributions that are 56 | not aligned to this Code of Conduct, and will communicate reasons for 57 | moderation decisions when appropriate. 58 | 59 | ## Scope 60 | 61 | This Code of Conduct applies within the community, and also applies when 62 | an individual is officially representing the community in public spaces. 63 | Examples of representing our community include using an official e-mail 64 | address, posting via an official social media account, or acting as an 65 | appointed representative at an online or offline event. 66 | 67 | ## Enforcement 68 | 69 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 70 | reported to the project team responsible for enforcement at 71 | {} All complaints will be reviewed and 72 | investigated promptly and fairly. 73 | 74 | All project team are obligated to respect the privacy and security of the 75 | reporter of any incident. 76 | 77 | ## Enforcement Guidelines 78 | 79 | The project team will follow these Community Impact Guidelines in determining 80 | the consequences for any action they deem in violation of this Code of Conduct: 81 | 82 | ### 1. Correction 83 | 84 | **Community Impact**: Use of inappropriate language or other behavior deemed 85 | unprofessional or unwelcome in the community. 86 | 87 | **Consequence**: A private, written warning from the project team, providing 88 | clarity around the nature of the violation and an explanation of why the 89 | behavior was inappropriate. A public apology may be requested. 90 | 91 | ### 2. Warning 92 | 93 | **Community Impact**: A violation through a single incident or series of 94 | actions. 95 | 96 | **Consequence**: A warning with consequences for continued behavior. No 97 | interaction with the people involved, including unsolicited interaction with 98 | those enforcing the Code of Conduct, for a specified period of time. This 99 | includes avoiding interactions in community spaces as well as external channels 100 | like social media. Violating these terms may lead to a temporary or permanent 101 | ban. 102 | 103 | ### 3. Temporary Ban 104 | 105 | **Community Impact**: A serious violation of community standards, including 106 | sustained inappropriate behavior. 107 | 108 | **Consequence**: A temporary ban from any sort of interaction or public 109 | communication with the community for a specified period of time. No public or 110 | private interaction with the people involved, including unsolicited 111 | interaction with those enforcing the Code of Conduct, is allowed during this 112 | period. Violating these terms may lead to a permanent ban. 113 | 114 | ### 4. Permanent Ban 115 | 116 | **Community Impact**: Demonstrating a pattern of violation of community 117 | standards, including sustained inappropriate behavior, harassment of an 118 | individual, or aggression toward or disparagement of classes of individuals. 119 | 120 | **Consequence**: A permanent ban from any sort of public interaction within the 121 | project community. 122 | 123 | ## Attribution 124 | 125 | This Code of Conduct is adapted from the [Contributor Covenant, version 2.0][cc-20-doc]. 126 | 127 | Community Impact Guidelines were inspired by 128 | [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 129 | 130 | [cc-20-doc]: https://www.contributor-covenant.org/version/2/0/code_of_conduct/ 131 | 132 | For answers to common questions about this code of conduct, see the FAQ at 133 | https://www.contributor-covenant.org/faq. Translations are available at 134 | https://www.contributor-covenant.org/translations. -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | 2 | # JetPath Contributing Guide 3 | 4 | 5 | Welcome to the JetPath Contributing Guide, and thank you for your interest. 6 | 7 | If you would like to contribute to a specific part of the project, check out the following list of contributions that we accept and their corresponding sections that are within this guide: 8 | 9 | * Documentation 10 | * Go to the `docs` dir 11 | * Bug Fixes 12 | * Go to source code dir at `src` 13 | * New Features 14 | * Create a sample of the new feature, and start a discussion in the community forum. 15 | 16 | However, at this time, we do not accept the following contributions: 17 | 18 | * Maintenance 19 | 20 | ## JetPath overview 21 | 22 | The purpose of the JetPath is to streamline your development process while offering flexibility in your choice of runtime environment 23 | 24 | ## Ground rules 25 | 26 | Before contributing, read our [Code of Conduct](https://github.com/CodeDynasty-dev/Jetpath?tab=coc-ov-file) to learn more about our community guidelines and expectations. 27 | 28 | ## Community engagement 29 | 30 | Refer to the following channels to connect with fellow contributors or to stay up-to-date with news about JetPath: 31 | 32 | * Join our project contributors on {name and link to online chat}.Discord 33 | * Participate in our project meetings on {specify the day of the week and cadence} at {specify time and timezone}, where you can provide a status update or raise questions and concerns about your contributions. Use the following link to join: {link to online meeting space} 34 | 2-3 meetings monthly, Fridays 35 | 36 | ## Before you start 37 | 38 | Before you start contributing, ensure you have the following: 39 | * For developers: The latest version of [Node.js](https://nodejs.org/en/download), [Bon.js](https://bun.sh/), [Deno.js](https://deno.com/). 40 | * For writers: The lastest version of [Node.js](https://nodejs.org/en/download). 41 | 42 | 43 | ## Environment setup 44 | 45 | To set up your environment, perform the following actions: 46 | 47 | ### Developer 48 | 49 | 1. Fork the Repository: Click the **Fork** button at the top right of the repository page to create a copy under your GitHub account. 50 | 51 | 2. Clone your forked repository to your computer using the command below. Replace `yourusername` with your GitHub username: 52 | 53 | ```bash 54 | git clone https://github.com//JetPath.git 55 | ``` 56 | 3. Navigate to the Project Directory: Change into the project folder using the command below. 57 | 58 | ```bash 59 | cd JetPath 60 | ``` 61 | 4. Install Dependencies: Install all necessary packages with npm: 62 | 63 | ```bash 64 | npm install 65 | 66 | ``` 67 | This will download and set up all libraries the project depends on. 68 | 69 | 5. Create a new branch for your feature or fix 70 | ```bash 71 | git checkout -b your-feature-branch 72 | ``` 73 | 6. Run the Development Server: Start the local server to preview your changes 74 | 75 | ```bash 76 | npm run dev 77 | ``` 78 | Open your browser and click the URL shown in the terminal (usually http://localhost:4000). 79 | 80 | 7. Compile the Project: Run the following command to build the project for production 81 | 82 | ```bash 83 | npm run compile 84 | ``` 85 | 8. Push your branch to your fork and open a Pull Request to the main repository. 86 | Feel free to ask questions or open an issue if you need help! 87 | 88 | 89 | ### Writers 90 | 91 | 92 | 1. Fork the Repository: Click the **Fork** button at the top right of the repository page to create a copy under your GitHub account. 93 | 94 | 2. Clone your forked repository to your computer using the command below. Replace with your GitHub username: 95 | 96 | ```bash 97 | git clone https://github.com//JetPath.git 98 | ``` 99 | 3. Navigate to the Project Directory: Change into the project folder using the command below. 100 | 101 | ```bash 102 | cd JetPath 103 | ``` 104 | 4. Install Dependencies 105 | ```bash 106 | npm install 107 | 108 | ``` 109 | 110 | 5. Create a new branch 111 | 112 | ```bash 113 | git checkout -b your-feature-branch 114 | ``` 115 | 6. Preview your changes with this command below 116 | 117 | ```bash 118 | npx docmach 119 | ``` 120 | 121 | 7. Push your branch to your fork and open a Pull Request to the main repository. 122 | 123 | Open your browser and click the URL shown in the terminal (usually http://localhost:4000). 124 | 125 | 126 | 127 | ### Troubleshoot 128 | 129 | If you encounter issues as you set up your environment, 130 | reach out to the team @fridaycandour for development and @NickyShe for documentation. 131 | 132 | 133 | ## Best practices 134 | 135 | Our project uses the [Google Typescript coding style guide](https://github.com/google/gts) as our parent guide for best practices. Reference the guide to familiarize yourself with the best practices we want contributors to follow 136 | ### Developers 137 | 138 | * Organize your code properly 139 | * Always run your test before pushing any code 140 | 141 | ### Writers 142 | 143 | Read the [Google developers documentation writing style guide](https://developers.google.com/style) to understand the guidelines for writing and formatting documents. The purpose of the style guide is to ensure consistency in the tone, voice, and structure of our documentation. 144 | 145 | ## Contribution workflow 146 | 147 | ### Report issues and bugs 148 | 149 | To help us improve JetPath, please report any issues or bugs you encounter. Here’s how you can do it: 150 | 151 | 1. **Check existing issues**: Before creating a new issue, search the issue tracker to see if someone else has already reported the problem. 152 | 2. **Create a new issue**: If you can’t find an existing issue, click the **New issue** button and fill out the template provided. Make sure to include: 153 | * Summarize the issue in a few words. 154 | * Explain the problem, including any error messages or unexpected behavior. 155 | * List the steps you took that led to the issue. 156 | * Describe what you expected to happen and what actually happened. 157 | * Attach any relevant screenshots or log files if applicable. 158 | 159 | 3. **Provide context**: If relevant, mention your environment for example, operating system, browser, version of JetPath. 160 | 161 | 4. **Use Labels**: Apply labels to categorize issues by type for eexample,bug, feature request, documentation. 162 | 5. **Prioritize**: Use priority labels for example, high, medium, low to indicate the urgency of the issue. 163 | 164 | 6. **Comment and discuss**: Use the issue comments to discuss the problem and potential solutions with other contributors. 165 | 166 | By following these steps, we can efficiently track and resolve issues, ensuring JetPath continues to improve for everyone. 167 | 168 | ### Commit messages 169 | Here are the types and description of commit messages 170 | 171 | | Type | Description | 172 | |----------|------------ | 173 | | feat | New feature | 174 | | fix | Bug fix | 175 | | docs | Documentation changes | 176 | | chore | Maintenance / build tasks | 177 | | refactor | Code refactoring without feature change | 178 | 179 | Example Commit message 180 | 181 | ```bash 182 | feat: add support for Deno.js runtime environment detection 183 | 184 | fix: resolve issue with npm run compile failing on Windows 185 | 186 | docs: update contributing guide with branch naming conventions 187 | 188 | chore: upgrade dependencies to latest stable versions 189 | 190 | refactor: reorganize src/utils for better modularity 191 | 192 | ``` 193 | 194 | ### Branch creation 195 | 196 | To keep our repository organized and make collaboration easier, please follow these guidelines when creating and naming branches: 197 | 198 | Use a consistent prefix to indicate the type of work: 199 | 200 | * feature/ for new features 201 | 202 | * bugfix/ for bug fixes 203 | 204 | * hotfix/ for urgent or critical fixes 205 | 206 | * release/ for release preparation 207 | 208 | * docs/ for documentation updates 209 | 210 | Example: 211 | ```bash 212 | git checkout -b feature/add-user-authentication 213 | git checkout -b bugfix/fix-login-error 214 | git checkout -b docs/update-contributing-guide 215 | git checkout -b release/1.0.0 216 | 217 | ``` 218 | Sticking to these conventions helps everyone quickly understand the purpose of each branch and keeps our workflow efficient 219 | 220 | ### Pull requests 221 | 222 | When your changes are ready, submit a pull request (PR) to propose merging your branch into the main repository. Please follow these steps: 223 | 224 | 1. Push your branch to your forked repository: 225 | 226 | ```bash 227 | git push -u origin your-branch-name 228 | ``` 229 | 2. Open a pull request on GitHub: 230 | 231 | * Navigate to the main repository on GitHub. 232 | 233 | * Click "Compare & pull request" next to your branch. 234 | 235 | * Ensure the base branch is main (or the appropriate target branch). 236 | 237 | 3. Fill out the pull request template: 238 | 239 | * Provide a clear title and description. 240 | 241 | * List the main changes and the motivation behind them. 242 | 243 | * Reference any related issues by number (e.g., fixes #42). 244 | 245 | Pull Request Template Example: 246 | ``` 247 | ## Description 248 | Briefly describe the purpose of this pull request. 249 | 250 | ## Changes 251 | - List key changes here 252 | 253 | ## Additional Information 254 | Add any extra context, screenshots, or testing instructions. 255 | 256 | ## Checklist 257 | - [ ] Tests passed 258 | - [ ] Documentation updated 259 | ``` 260 | 261 | ### Releases 262 | 263 | {Provide a description of the release process and cadence for the project, such as the source code.} 264 | 265 | 266 | --- 267 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@codedynasty/jetpath", 3 | "version": "1.4.17", 4 | "exports": "./dist/index.js", 5 | "ustable": [ 6 | "byonm" 7 | ] 8 | } -------------------------------------------------------------------------------- /docs/assets/custom-typography.css: -------------------------------------------------------------------------------- 1 | /* Custom Typography Styles */ 2 | :root { 3 | --font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; 4 | 5 | /* Font Sizes */ 6 | --font-size-xs: 0.75rem; 7 | --font-size-sm: 0.875rem; 8 | --font-size-base: 1rem; 9 | --font-size-lg: 1.125rem; 10 | --font-size-xl: 1.25rem; 11 | --font-size-2xl: 1.5rem; 12 | --font-size-3xl: 1.875rem; 13 | --font-size-4xl: 2.25rem; 14 | --font-size-5xl: 3rem; 15 | --font-size-6xl: 4rem; 16 | 17 | /* Line Heights */ 18 | --line-height-tight: 1.25; 19 | --line-height-normal: 1.5; 20 | --line-height-relaxed: 1.75; 21 | --line-height-loose: 2; 22 | } 23 | 24 | /* Base Typography */ 25 | body { 26 | font-family: var(--font-family); 27 | line-height: var(--line-height-normal); 28 | } 29 | 30 | /* Headings */ 31 | h1, h2, h3, h4, h5, h6 { 32 | font-weight: 600; 33 | line-height: var(--line-height-tight); 34 | margin-bottom: 1rem; 35 | } 36 | 37 | h1 { 38 | font-size: var(--font-size-4xl); 39 | color: var(--text); 40 | } 41 | 42 | h2 { 43 | font-size: var(--font-size-3xl); 44 | color: var(--text); 45 | border-bottom: 1px solid var(--border); 46 | padding-bottom: 0.5rem; 47 | } 48 | 49 | h3 { 50 | font-size: var(--font-size-2xl); 51 | color: var(--text); 52 | } 53 | 54 | h4 { 55 | font-size: var(--font-size-xl); 56 | color: var(--text-light); 57 | } 58 | 59 | h5 { 60 | font-size: var(--font-size-lg); 61 | color: var(--text-light); 62 | } 63 | 64 | h6 { 65 | font-size: var(--font-size-base); 66 | color: var(--text-light); 67 | } 68 | 69 | /* Paragraphs */ 70 | p { 71 | font-size: var(--font-size-base); 72 | color: var(--text-light); 73 | margin-bottom: 1.5rem; 74 | } 75 | 76 | /* Lists */ 77 | ul, ol { 78 | margin-bottom: 1.5rem; 79 | padding-left: 1.5rem; 80 | } 81 | 82 | li { 83 | margin-bottom: 0.5rem; 84 | } 85 | 86 | /* Code Blocks */ 87 | pre { 88 | font-family: 'JetBrains Mono', monospace; 89 | font-size: 0.875rem; 90 | background: rgba(16, 16, 16, 0.9); 91 | padding: 1.5rem; 92 | border-radius: 0.5rem; 93 | overflow-x: auto; 94 | margin-bottom: 1.5rem; 95 | } 96 | 97 | code { 98 | font-family: 'JetBrains Mono', monospace; 99 | font-size: 0.875rem; 100 | background: rgba(16, 16, 16, 0.9); 101 | padding: 0.25rem 0.5rem; 102 | border-radius: 0.25rem; 103 | color: #7f99ff; 104 | } 105 | 106 | /* Blockquotes */ 107 | blockquote { 108 | border-left: 3px solid var(--primary); 109 | padding: 1rem 1.5rem; 110 | margin: 1.5rem 0; 111 | background: rgba(16, 16, 16, 0.9); 112 | color: var(--text-light); 113 | } 114 | 115 | /* Tables */ 116 | table { 117 | width: 100%; 118 | border-collapse: collapse; 119 | margin-bottom: 1.5rem; 120 | } 121 | 122 | th, td { 123 | padding: 0.75rem; 124 | text-align: left; 125 | border-bottom: 1px solid var(--border); 126 | } 127 | 128 | th { 129 | font-weight: 600; 130 | background: rgba(16, 16, 16, 0.9); 131 | } 132 | 133 | /* Links */ 134 | a { 135 | color: var(--primary); 136 | text-decoration: none; 137 | transition: color 0.2s ease; 138 | } 139 | 140 | a:hover { 141 | color: var(--primary-dark); 142 | } 143 | 144 | /* Emphasis */ 145 | strong { 146 | color: var(--text); 147 | } 148 | 149 | em { 150 | color: var(--text-light); 151 | } 152 | 153 | /* Horizontal Rules */ 154 | hr { 155 | border: none; 156 | border-top: 1px solid var(--border); 157 | margin: 2rem 0; 158 | } 159 | 160 | /* Responsive Typography */ 161 | @media (max-width: 768px) { 162 | h1 { 163 | font-size: var(--font-size-3xl); 164 | } 165 | 166 | h2 { 167 | font-size: var(--font-size-2xl); 168 | } 169 | 170 | h3 { 171 | font-size: var(--font-size-xl); 172 | } 173 | 174 | h4 { 175 | font-size: var(--font-size-lg); 176 | } 177 | 178 | pre { 179 | font-size: 0.8125rem; 180 | } 181 | } 182 | 183 | 184 | .token.string { 185 | color: gold !important; 186 | text-shadow: 0px 0px 12px gold !important; 187 | } 188 | 189 | .prose a, .prose strong, .prose em { 190 | color: var(--secondary); 191 | text-decoration: none; 192 | transition: color 0.2s ease; 193 | } 194 | 195 | /* mobile styles */ 196 | @media (max-width: 768px) { 197 | .prose { 198 | font-size: var(--font-size-base); 199 | } 200 | 201 | .prose h1 { 202 | font-size: var(--font-size-2xl); 203 | } 204 | 205 | .prose h2 { 206 | font-size: var(--font-size-xl); 207 | } 208 | 209 | .prose h3 { 210 | font-size: var(--font-size-lg); 211 | } 212 | 213 | .prose h4 { 214 | font-size: var(--font-size-base); 215 | } 216 | 217 | .header { 218 | padding: 0; 219 | } 220 | } -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeDynasty-dev/Jetpath/ec22ef8a7e056071590def7c0be943ed464398e7/docs/assets/favicon.ico -------------------------------------------------------------------------------- /docs/assets/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeDynasty-dev/Jetpath/ec22ef8a7e056071590def7c0be943ed464398e7/docs/assets/home.png -------------------------------------------------------------------------------- /docs/assets/icon-transparent.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeDynasty-dev/Jetpath/ec22ef8a7e056071590def7c0be943ed464398e7/docs/assets/icon-transparent.webp -------------------------------------------------------------------------------- /docs/assets/icon.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeDynasty-dev/Jetpath/ec22ef8a7e056071590def7c0be943ed464398e7/docs/assets/icon.webp -------------------------------------------------------------------------------- /docs/assets/index.css: -------------------------------------------------------------------------------- 1 | :root{--primary:#fff;--primary-dark:#6769a9;--secondary:#3B82F6;--secondary-dark:#2563EB;--dark:#0A0A0A;--dark-light:#121212;--dark-lighter:#1A1A1A;--text:#F9FAFB;--text-light:#D1D5DB;--text-lighter:#9CA3AF;--border-color:rgba(255,255,255,0.1);--border-light:rgba(255,255,255,0.05)}body{background-color:var(--dark);color:var(--text);font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overflow-x:hidden;line-height:1.6}a{color:inherit;text-decoration:none}.container{width:100%;max-width:1200px;margin:0 auto;padding:0 1.5rem}.header{position:fixed;top:0;left:0;right:0;z-index:100;background:rgba(10,10,10,0.8);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-bottom:1px solid rgba(255,255,255,0.1);padding:1rem 0;transition:all 0.3s ease}.header-scrolled{background:rgba(10,10,10,0.95);box-shadow:0 4px 20px rgba(0,0,0,0.3)}.header-container{display:flex;justify-content:space-between;align-items:center}.logo{display:flex;align-items:center;font-weight:700;font-size:1.5rem}.logo-text{background:linear-gradient(90deg,var(--primary),var(--secondary));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;color:transparent}.nav{display:flex;align-items:center;gap:2rem}.nav-link{font-weight:500;transition:color 0.2s ease;position:relative}.nav-link::after{content:'';position:absolute;bottom:-4px;left:0;width:0;height:2px;background:linear-gradient(90deg,var(--primary),var(--secondary));transition:width 0.3s ease}.nav-link:hover::after{width:100%}.nav-link:hover{color:var(--primary)}.btn{display:inline-block;padding:0.75rem 1.5rem;border-radius:0.5rem;font-weight:600;transition:all 0.3s ease;cursor:pointer}.btn-primary{background:linear-gradient(135deg,var(--primary),var(--secondary));color:var(--dark);border:none;box-shadow:0 4px 14px rgba(59,130,246,0.3)}.btn-primary:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(59,130,246,0.4)}.btn-outline{background:transparent;color:var(--text);border:1px solid rgba(255,255,255,0.1)}.btn-outline:hover{border-color:var(--primary);color:var(--primary)}.mobile-menu-btn{background:transparent none;font-size:1.5rem;display:none;padding:0.5rem;border:none;background:transparent;cursor:pointer;z-index:100}.hero{padding-top:15rem;padding-bottom:6rem;position:relative;overflow:hidden}.hero::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background-image:radial-gradient(circle at 20% 30%,rgb(59 130 246) 0%,transparent 50%),radial-gradient(circle at 80% 70%,rgb(37 99 235) 0%,transparent 50%);z-index:-1}.hero-grid{position:absolute;top:0;left:0;right:0;bottom:0;background-image:linear-gradient(rgba(255,255,255,0.02) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,0.02) 1px,transparent 1px);background-size:30px 30px;z-index:-1}.hero-content{display:flex;flex-direction:column;align-items:center;text-align:center;max-width:900px;margin:0 auto}.hero-badge{background:#2563eb;border:1px solid rgba(110,231,183,0.2);color:var(--primary);font-size:0.875rem;font-weight:500;padding:0.5rem 1rem;border-radius:2rem;margin-bottom:1.5rem}.hero-title{font-size:3.5rem;font-weight:800;line-height:1.2;margin-bottom:1.5rem;background:linear-gradient(to right,var(--primary),var(--secondary));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;color:transparent}.hero-subtitle{font-size:1.25rem;color:var(--text-light);max-width:700px;margin-bottom:2.5rem}.hero-cta{display:flex;gap:1rem;margin-bottom:4rem}.code-demo{width:100%;max-width:800px;margin:0 auto;border-radius:0.75rem;overflow:hidden;box-shadow:0 20px 50px rgba(0,0,0,0.5);transform:translateY(0);transition:transform 0.3s ease}.code-demo:hover{transform:translateY(-5px)}.code-header{display:flex;align-items:center;background:#1E1E1E;padding:0.75rem 1rem;border-bottom:1px solid rgba(255,255,255,0.1)}.code-dots{display:flex;gap:0.375rem;margin-right:1rem}.code-dot{width:0.75rem;height:0.75rem;border-radius:50%}.code-dot.red{background-color:#FF5F56}.code-dot.yellow{background-color:#FFBD2E}.code-dot.green{background-color:#27C93F}.code-tabs{display:flex;gap:1rem}.code-tab{font-size:0.875rem;color:var(--text-lighter);cursor:pointer;padding-bottom:0.25rem;border-bottom:2px solid transparent;transition:all 0.2s ease}.code-tab.active{color:var(--primary);border-bottom-color:var(--primary)}.code-content{background:#1E1E1E;position:relative}.code-wrapper{display:none}.code-wrapper.active{display:block}.copy-btn{position:absolute;top:1rem;right:1rem;background:rgba(255,255,255,0.1);border:none;color:var(--text-lighter);padding:0.375rem 0.75rem;border-radius:0.25rem;font-size:0.75rem;cursor:pointer;transition:all 0.2s ease;z-index:2}.copy-btn:hover{background:rgba(255,255,255,0.2);color:var(--text)}pre{margin:0 0!important;padding:1.5rem 1.5rem!important;font-size:0.9rem;line-height:1.6;border-radius:0}code{font-family:'Menlo','Monaco','Consolas',monospace}.section{padding:6rem 0}.section-title{text-align:center;margin-bottom:1rem;text-align:center;font-size:2.25rem;text-align:center;margin-bottom:3rem}.section-title span{background:linear-gradient(to right,var(--primary),var(--secondary));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;color:transparent}.section-subtitle{font-size:1.125rem;color:var(--text-light);text-align:center;max-width:700px;margin:0 auto 3rem}.features-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:2rem}.feature-card{background:var(--dark-lighter);border:1px solid rgba(255,255,255,0.1);border-radius:1rem;padding:2rem;transition:all 0.3s ease;height:100%;display:flex;flex-direction:column}.feature-card:hover{transform:translateY(-5px);box-shadow:0 10px 30px rgba(0,0,0,0.2);border-color:rgb(37 99 235 / 57%)}.feature-icon{width:3rem;height:3rem;background:linear-gradient(135deg,#2563eb,rgba(59,130,246,0.2));border-radius:0.75rem;display:flex;align-items:center;justify-content:center;margin-bottom:1.5rem}.feature-icon svg{width:1.5rem;height:1.5rem;color:var(--primary)}.feature-title{font-size:1.25rem;font-weight:600;margin-bottom:0.75rem}.feature-description{color:var(--text-light);font-size:1rem;line-height:1.6;flex-grow:1}.why-jetpath{background:linear-gradient(to bottom,var(--dark),var(--dark-light))}.comparison-table{width:100%;background:var(--dark-lighter);border-radius:1rem;overflow:hidden;border:1px solid rgba(255,255,255,0.1);margin-top:2rem;box-shadow:0 10px 30px rgba(0,0,0,0.2)}.comparison-table thead{background:#2563eb}.comparison-table th,.comparison-table td{padding:1rem 1.5rem;text-align:left;border-bottom:1px solid rgba(255,255,255,0.1)}.comparison-table th{font-weight:600;color:var(--text)}.comparison-table td{color:var(--text-light)}.comparison-table tr:last-child td{border-bottom:0}.highlight-cell{color:var(--primary)!important}.performance{background:var(--dark);position:relative;overflow:hidden}.performance::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:radial-gradient(circle at 10% 90%,rgba(110,231,183,0.08) 0%,transparent 50%);z-index:-1}.benchmark-container{background:var(--dark-lighter);border:1px solid rgba(255,255,255,0.1);border-radius:1rem;padding:2rem;margin-top:3rem;box-shadow:0 10px 30px rgba(0,0,0,0.2)}.benchmark-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:2rem;margin-top:2rem}.benchmark-card{text-align:center}.benchmark-value{font-size:2.5rem;font-weight:700;background:linear-gradient(to right,var(--primary),var(--secondary));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;color:transparent;margin-bottom:0.5rem}.benchmark-label{color:var(--text-light);font-size:0.875rem}.ecosystem{background:linear-gradient(to bottom,var(--dark-light),var(--dark))}.ecosystem-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:2rem}.ecosystem-card{background:var(--dark-lighter);border:1px solid rgba(255,255,255,0.1);border-radius:1rem;padding:2rem;transition:all 0.3s ease;text-align:center}.ecosystem-card:hover{transform:translateY(-5px);box-shadow:0 10px 30px rgba(0,0,0,0.2);border-color:rgba(59,130,246,0.3)}.ecosystem-icon{width:3.5rem;height:3.5rem;margin:0 auto 1.5rem;background:linear-gradient(135deg,#2563eb,rgba(10,10,10,0.95));border-radius:50%;display:flex;align-items:center;justify-content:center}.ecosystem-icon svg{width:1.75rem;height:1.75rem;color:var(--secondary)}.ecosystem-title{font-size:1.25rem;font-weight:600;margin-bottom:0.75rem}.ecosystem-description{color:var(--text-light);font-size:1rem}.cta{background:linear-gradient(to bottom right,var(--dark-light),#0B1120);padding:6rem 0;position:relative;overflow:hidden}.cta::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:radial-gradient(circle at 80% 20%,rgba(59,130,246,0.15) 0%,transparent 50%),radial-gradient(circle at 20% 80%,rgba(110,231,183,0.15) 0%,transparent 50%);z-index:-1}.cta-content{max-width:600px;margin:0 auto;text-align:center}.cta-title{font-size:2.5rem;font-weight:800;margin-bottom:1.5rem}.cta-description{color:var(--text-light);font-size:1.125rem;margin-bottom:2.5rem}.cta-buttons{display:flex;justify-content:center;gap:1rem}.footer{background:var(--dark);padding:4rem 0 2rem;border-top:1px solid rgba(255,255,255,0.1)}.footer-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:3rem;margin-bottom:3rem}.footer-brand{grid-column:1 / -1}@media (min-width:768px){.footer-brand{grid-column:auto}}.footer-logo{font-size:1.5rem;font-weight:700;margin-bottom:1rem;background:linear-gradient(90deg,var(--primary),var(--secondary));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;color:transparent}.footer-tagline{color:var(--text-light);font-size:0.875rem;margin-bottom:1.5rem}.footer-social{display:flex;gap:1rem}.footer-social-link{width:2rem;height:2rem;background:rgba(255,255,255,0.05);border-radius:50%;display:flex;align-items:center;justify-content:center;color:var(--text-light);transition:all 0.2s ease}.footer-social-link:hover{background:#2563eb;color:var(--primary);transform:translateY(-3px)}.footer-heading{font-size:1rem;font-weight:600;margin-bottom:1.25rem}.footer-links{list-style:none}.footer-link{margin-bottom:0.75rem}.footer-link a{color:var(--text-light);font-size:0.9rem;transition:color 0.2s ease}.footer-link a:hover{color:var(--primary)}.footer-bottom{border-top:1px solid rgba(255,255,255,0.1);padding-top:2rem;text-align:center;color:var(--text-lighter);font-size:0.875rem}@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-15px)}}.float-animation{animation:float 6s ease-in-out infinite}@media (max-width:768px){.nav{display:none;position:absolute;top:100%;left:0;right:0;flex-direction:column;align-items:center;gap:1rem;padding:1.5rem;background:rgba(10,10,10,0.95);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-bottom:1px solid rgba(255,255,255,0.1)}.nav.active{display:flex}.mobile-menu-btn{display:block}.hero-title{font-size:2.5rem}.hero-cta{flex-direction:column}.code-demo{border-radius:0.5rem}.section-title{font-size:2rem}.cta-title{font-size:2rem}.cta-buttons{flex-direction:column}}.custom-shape{position:absolute;width:500px;height:500px;filter:blur(100px);opacity:0.15;z-index:-1}.shape-1{background:var(--primary);top:10%;right:-200px;border-radius:100% 0 0 100%}.shape-2{background:var(--secondary);bottom:-200px;left:-100px;border-radius:100%}.interactive-features{display:flex;flex-wrap:wrap;justify-content:center;gap:1rem;margin-bottom:3rem}.feature-btn{background:var(--dark-lighter);border:1px solid rgba(255,255,255,0.1);color:var(--text-light);padding:0.75rem 1.5rem;border-radius:0.5rem;font-weight:500;cursor:pointer;transition:all 0.3s ease}.feature-btn:hover,.feature-btn.active{background:linear-gradient(135deg,#2563eb,rgba(59,130,246,0.1));border-color:rgb(37 99 235 / 57%);color:var(--primary);transform:translateY(-3px)}.terminal{position:relative;width:100%;max-width:800px;margin:3rem auto 0;border-radius:0.75rem;overflow:hidden;box-shadow:0 20px 50px rgba(0,0,0,0.5)}.terminal-header{display:flex;align-items:center;background:#1E1E1E;padding:0.75rem 1rem;border-bottom:1px solid rgba(255,255,255,0.1)}.terminal-title{margin-left:1rem;font-size:0.875rem;color:var(--text-lighter)}.terminal-body{background:#0D1017;padding:1.5rem;font:0.875rem/1.7 'Menlo','Monaco','Consolas',monospace;color:var(--text-light)}.terminal-line{display:flex;margin-bottom:0.5rem}.terminal-prompt{color:var(--primary);margin-right:0.75rem;user-select:none}.terminal-output{opacity:0;animation:fadeIn 0.5s ease forwards}@keyframes fadeIn{from{opacity:0}to{opacity:1}}.terminal-cursor{display:inline-block;width:0.5rem;height:1.2rem;background:var(--primary);margin-left:2px;animation:blink 1s infinite}@keyframes blink{0%,100%{opacity:1}50%{opacity:0}}.terminal-typing{position:relative}.terminal-typing::after{content:'|';position:absolute;right:-10px;animation:blink 1s infinite}.cursor-highlight{position:fixed;width:300px;height:300px;border-radius:50%;pointer-events:none;background:radial-gradient(circle,#2563eb 0%,rgba(110,231,183,0) 70%);transform:translate(-50%,-50%);z-index:-1;opacity:0;transition:opacity 0.3s ease-out}.section-padding{padding:5rem 1.5rem}@media (min-width:768px){.section-padding{padding-left:3rem;padding-right:3rem}}@media (min-width:1024px){.section-padding{padding-left:6rem;padding-right:6rem}}@media (min-width:768px){.section-title{font-size:2.75rem;margin-bottom:4rem}}.glassmorphism{background:rgba(16,16,16,0.7);border-radius:16px;box-shadow:0 4px 30px rgba(0,0,0,0.2);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,0.15);padding:8px;margin-top:1rem}.red{border:1px red solid}.red-a *{border:1px red solid}.logo-container{display:flex;align-items:center;gap:1rem}.search-bar-container{width:100%;flex-grow:1;max-width:400px}.search-bar{position:relative;display:flex;align-items:center;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.1);border-radius:0.5rem;padding:0.5rem 1rem;transition:all 0.3s ease;width:100%;max-width:100%}.search-bar:focus-within{border-color:var(--primary);box-shadow:0 0 0 2px rgba(110,231,183,0.2)}.search-input{background:transparent;border:none;color:var(--text);font-size:1rem;width:100%;outline:0}.search-bar svg{margin-right:0.5rem}@media (max-width:768px){.header-container{display:grid;grid-template-rows:1fr 1fr}.shape-1,.shape-2{display:none}.header-container.container{width:100%;padding:12px;display:flex;justify-content:center;align-items:center;flex-direction:column}.search-bar-container{flex-basis:100%;max-width:100%;margin-bottom:0.5rem}.header-container .flex.w-full.justify-between{min-width:80vw;margin:0 auto}.search-bar{max-width:100%}}:not(pre)>code[class*=language-],pre[class*=language-]{background-color:#000523!important}.mobile-menu-btn svg{width:24px;height:24px}.mobile-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:85}@media (max-width:1024px){.mobile-menu-btn{display:block}#doc-side-bar{position:fixed;top:0;left:-256px;width:256px;min-height:100vh;height:100%;background:rgba(10,10,10,0.8);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-right:1px solid rgba(255,255,255,0.1);z-index:90;padding:2rem 1rem;transition:left 0.3s ease}#doc-side-bar.active{left:0;display:block}.mobile-overlay.active{display:block}.main-content{margin-left:0;padding:4rem 1.5rem 1.5rem}}@media (min-width:768px){.footer-brand{grid-column:auto}}@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-15px)}}@media (max-width:768px){.nav{display:none;position:absolute;top:100%;left:0;right:0;flex-direction:column;align-items:center;gap:1rem;padding:1.5rem;background:rgba(10,10,10,0.95);backdrop-filter:blur(10px);-webkit-backpack-filter:blur(10px);border-bottom:1px solid rgba(255,255,255,0.1)}.nav.active{display:flex}.mobile-menu-btn{display:block}.hero-title{font-size:2.5rem}.hero-cta{flex-direction:column}.code-demo{border-radius:0.5rem}.section-title{font-size:2rem}.cta-title{font-size:2rem}.cta-buttons{flex-direction:column}}@keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes blink{0%,100%{opacity:1}50%{opacity:0}}@media (min-width:768px){.section-padding{padding-left:3rem;padding-right:3rem}}@media (min-width:1024px){.section-padding{padding-left:6rem;padding-right:6rem}}@media (min-width:768px){.section-title{font-size:2.75rem;margin-bottom:4rem}}@media (max-width:768px){.header-container{display:grid;grid-template-rows:1fr 1fr}.shape-1,.shape-2{display:none}.header-container.container{width:100%;padding:0px;display:flex;justify-content:center;align-items:center;flex-direction:column}.search-bar-container{flex-basis:100%;max-width:100%;margin-bottom:0.5rem}.header-container .flex.w-full.justify-between{min-width:80vw;margin:0 auto}.search-bar{max-width:100%}}#doc-side-bar{position:fixed;top:0;left:0;width:256px;height:100vh;background:rgba(10,10,10,0.8);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-right:1px solid rgba(255,255,255,0.1);z-index:90;}.main-content{margin-left:256px;padding:4rem 1.5rem 1.5rem}@media (max-width:1024px){#doc-side-bar{display:none}.main-content{margin-left:0;padding:4rem 1.5rem 1.5rem}} -------------------------------------------------------------------------------- /docs/assets/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.30.0 2 | https://prismjs.com/download#themes=prism-okaidia&languages=markup+css+clike+javascript+http+typescript */ 3 | code[class*=language-],pre[class*=language-]{color:#f8f8f2;background:0 0;text-shadow:0 1px rgba(0,0,0,.3);font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background:#272822}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8292a2}.token.punctuation{color:#f8f8f2}.token.namespace{opacity:.7}.token.constant,.token.deleted,.token.property,.token.symbol,.token.tag{color:#f92672}.token.boolean,.token.number{color:#ae81ff}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#a6e22e}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url,.token.variable{color:#f8f8f2}.token.atrule,.token.attr-value,.token.class-name,.token.function{color:#e6db74}.token.keyword{color:#66d9ef}.token.important,.token.regex{color:#fd971f}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help} 4 | -------------------------------------------------------------------------------- /docs/discord/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CodeDynasty.dev on Discord 7 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /docs/docs/credits.md: -------------------------------------------------------------------------------- 1 | # Hello world -------------------------------------------------------------------------------- /docs/docs/docs/deployment.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Deployment 4 | 5 | Deploying your Jetpath application involves packaging your code and dependencies, choosing a hosting environment, and running the application server so it's accessible to users. Jetpath's flexibility allows you to deploy it across various platforms using Node.js, Deno, or Bun as the runtime. 6 | 7 | --- 8 | 9 | ## General Considerations Before Deployment 10 | 11 | Regardless of your target platform, consider these points: 12 | 13 | 1. **Build Step (TypeScript Compilation):** 14 | * Jetpath applications are typically written in TypeScript (`.jet.ts`, `.ts`). 15 | * **Node.js/Bun (often):** You'll usually need to compile your TypeScript code to JavaScript using `tsc` (based on your `tsconfig.json`) or Bun's built-in bundler/transpiler before deployment. Your `server.ts` entry point will then run the compiled JavaScript output (e.g., `node dist/server.js`). 16 | * **Deno:** Deno can run TypeScript directly. You might still have a build step for bundling or type checking (`deno check server.ts`), but compilation to JS isn't strictly necessary for running. 17 | * Ensure your build output includes all necessary JavaScript files. 18 | 19 | 2. **Dependencies:** 20 | * **Node.js/Bun:** Your `node_modules` directory (containing dependencies listed in `package.json`) must be included in the deployment or installed on the target server using `npm install --production`, `yarn install --production`, or `bun install --production`. 21 | * **Deno:** Dependencies are typically cached based on imports. Ensure the deployment environment has network access to download dependencies on first run, or use `deno vendor` to vendor dependencies locally before deployment. 22 | 23 | 3. **Runtime Choice:** 24 | * Ensure the exact runtime (Node.js, Deno, or Bun) and version you developed with is installed and available in your deployment environment (VM, container image, PaaS setting). 25 | 26 | 4. **Environment Variables:** 27 | * **Never hardcode secrets** (API keys, database passwords, JWT secrets) in your code. 28 | * Use environment variables (`process.env.VARIABLE_NAME`) to manage configuration. 29 | * Most hosting platforms provide a way to securely set environment variables for your application. Consult your platform's documentation. 30 | 31 | 5. **Port Binding:** 32 | * Your Jetpath application listens on a port specified in the `Jetpath` constructor (`port: 3000`) or potentially via an environment variable (e.g., `process.env.PORT`). 33 | * Most hosting platforms (PaaS, Containers) automatically route external traffic (on port 80/443) to the port your application listens on internally. Ensure your application respects the `PORT` environment variable if provided by the platform. 34 | 35 | 6. **Static Files:** 36 | * If your application serves static files (CSS, JS, images) using Jetpath's `static` option, ensure these files are included in your deployment package and the paths are configured correctly. Often, it's more performant to serve static files directly via a reverse proxy (like Nginx) or a CDN. 37 | 38 | --- 39 | 40 | ## Common Deployment Strategies 41 | 42 | Here are outlines for common deployment approaches: 43 | 44 | ### 1. Virtual Machines (VMs) / Bare Metal 45 | 46 | * **Concept:** You manage the operating system, runtime installation, and application deployment manually or via configuration management tools. 47 | * **Steps:** 48 | 1. Provision a VM (e.g., AWS EC2, Google Compute Engine, DigitalOcean Droplet) or prepare a physical server. 49 | 2. Install your chosen runtime (Node.js, Deno, or Bun). 50 | 3. Install necessary tools (Git, potentially a process manager like PM2). 51 | 4. Clone your application repository or copy your built application code (including `node_modules` or vendor directory). 52 | 5. Install production dependencies if needed. 53 | 6. Set environment variables (e.g., using `.env` files with `dotenv` or system environment variables). 54 | 7. Start your application server (e.g., `node dist/server.js`, `deno run --allow-net server.ts`, `bun run server.ts`). 55 | 8. **(Recommended)** Use a process manager (like PM2 for Node.js/Bun, or systemd for any runtime) to automatically restart your app if it crashes and manage logs. 56 | 9. **(Recommended)** Set up a reverse proxy (like Nginx or Caddy) in front of your Jetpath app to handle TLS/SSL termination (HTTPS), load balancing (if running multiple instances), basic caching, and potentially serving static files directly. 57 | 58 | ### 2. Containers (Docker) 59 | 60 | * **Concept:** Package your application, its runtime, and dependencies into a standardized container image. Deploy this image using container orchestration platforms. 61 | * **Steps:** 62 | 1. **Create a `Dockerfile`:** 63 | * Start with a base image for your chosen runtime (e.g., `node:18-alpine`, `denoland/deno:latest`, `oven/bun:latest`). 64 | * Set the working directory (e.g., `/app`). 65 | * Copy `package.json`, `*.lockb`, `deno.json` etc. and install dependencies (`npm install --production`, `deno cache`, `bun install --production`). Use multi-stage builds to keep the final image small. 66 | * Copy your application source code. 67 | * Perform the build step if necessary (`RUN tsc` or `RUN bun build`). 68 | * Expose the port your Jetpath application listens on (`EXPOSE 3000`). 69 | * Set the command to run your application (`CMD ["node", "dist/server.js"]` or `CMD ["deno", "run", "--allow-net", "server.ts"]` or `CMD ["bun", "run", "server.ts"]`). 70 | 2. **Build the Docker Image:** `docker build -t my-jetpath-app .` 71 | 3. **Run the Container:** `docker run -p 3000:3000 -e "NODE_ENV=production" -e "DATABASE_URL=..." my-jetpath-app` (Map port, pass environment variables). 72 | 4. **Deploy:** Push the image to a registry (Docker Hub, AWS ECR, Google Artifact Registry) and deploy it using platforms like Kubernetes, AWS ECS, Google Cloud Run, Docker Swarm, etc. These platforms handle scaling, networking, and health checks. 73 | 74 | ### 3. Platform-as-a-Service (PaaS) 75 | 76 | * **Concept:** Abstract away server management. You push your code, and the platform handles building, deploying, scaling, and routing. 77 | * **Platforms:** Heroku, Render, Fly.io, Railway, Vercel (Node.js focus), Deno Deploy (Deno focus). 78 | * **Steps:** 79 | 1. Choose a PaaS provider that supports your chosen runtime (Node.js, Deno, or Bun). 80 | 2. Configure your project according to the platform's requirements: 81 | * **Procfile (Heroku, others):** Define how to start your web process (e.g., `web: node dist/server.js`). 82 | * **Build Scripts (`package.json`):** Define `build` and `start` scripts. 83 | * **Runtime Configuration:** Specify the runtime version (e.g., `engines` in `package.json`, `deno.json`, platform-specific config files). 84 | * **Environment Variables:** Configure secrets and settings through the platform's dashboard or CLI. 85 | 3. Connect your Git repository to the platform. 86 | 4. Push your code. The platform will detect your project type, install dependencies, run your build script, and deploy your application. 87 | 88 | ### 4. Serverless Functions 89 | 90 | * **Concept:** Run your code in response to events (like HTTP requests) without managing servers. Pay per execution. 91 | * **Platforms:** AWS Lambda, Google Cloud Functions, Azure Functions, Cloudflare Workers. 92 | * **Steps:** 93 | 1. **Adapter/Wrapper:** This is often the trickiest part. Serverless platforms have specific event formats. You typically need an **adapter** layer to translate the platform's event into a standard `Request` object that Jetpath can understand and convert Jetpath's response back into the format the platform expects. Look for official or community-provided adapters for running generic web frameworks (or specifically Jetpath, if available) on your target serverless platform. Without an adapter, significant manual integration is required. 94 | 2. **Packaging:** Bundle your Jetpath application code, dependencies (including the adapter), and any build output into a deployment package (e.g., a zip file). 95 | 3. **Configuration:** Define the function handler (pointing to the adapter's entry point), memory allocation, timeout, and environment variables. 96 | 4. **API Gateway:** Configure an API Gateway service (like AWS API Gateway or Google Cloud API Gateway) to create an HTTP endpoint that triggers your serverless function. 97 | 98 | --- 99 | 100 | ## Jetpath Specifics & Recommendations 101 | 102 | * **Cross-Runtime:** The main advantage is choosing the best platform *and* runtime combination for your needs. Docker and VMs offer the most flexibility here. PaaS support might vary depending on how well they support Deno and Bun compared to Node.js. Serverless often requires runtime-specific adapters. 103 | * **Configuration:** Rely heavily on environment variables for configuration across all deployment types. 104 | * **Build Output:** Ensure your deployment process correctly includes the compiled JavaScript output (`dist` folder or similar) if applicable for your runtime choice. 105 | * **Process Management:** For VM/Bare Metal, always use a process manager (PM2, systemd) to ensure your app restarts on failure. Containers and PaaS handle this automatically. 106 | * **Logging:** Configure structured logging (e.g., JSON format) that can be easily ingested by your chosen platform's logging service (CloudWatch, Google Cloud Logging, Datadog, etc.). 107 | 108 | --- 109 | 110 | ## Next Steps 111 | 112 | * Consult the documentation for your chosen **hosting platform** and **runtime** for detailed, platform-specific instructions. 113 | * Review Jetpath's [**Configuration**](./configuration.md) options. 114 | * Implement robust [**Logging**](#observability) for monitoring your deployed application. *(Assuming an Observability page exists)* 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /docs/docs/docs/error-handling.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Error Handling 4 | 5 | Robust error handling is essential for creating reliable APIs. Jetpath centralizes error handling in middleware for consistent error management. 6 | 7 | ## Philosophy 8 | 9 | Instead of scattered `try...catch` blocks, Jetpath's approach relies on: 10 | 11 | 1. **Signaling Errors:** Using `throw new Error()` 12 | 2. **Centralized Catching:** Intercepting errors in middleware 13 | 3. **Standardized Responses:** Sending consistent error responses 14 | 15 | ## Throwing Errors 16 | 17 | When processing cannot continue normally, signal an error using `ctx.send(error, )`. 18 | 19 | ### Using `ctx.send(error, )` 20 | 21 | - **Common Status Codes:** 22 | - `400 Bad Request`: Validation errors 23 | - `401 Unauthorized`: Authentication issues 24 | - `403 Forbidden`: Permission denied 25 | - `404 Not Found`: Resource not found 26 | - `500 Internal Server Error`: Unexpected issues 27 | 28 | - **Examples:** 29 | ```typescript 30 | // Resource Not Found 31 | if (!pet) { 32 | ctx.send(`Pet ${ctx.params.id} not found`, 404); 33 | } 34 | 35 | // Unauthorized Access 36 | if (!ctx.plugins.verifyAuth(ctx).authenticated) { 37 | ctx.set("WWW-Authenticate", "Bearer realm=\"protected\""); 38 | ctx.send("Authentication required", 401); 39 | } 40 | 41 | // Forbidden Action 42 | if (ctx.app.user?.role !== 'admin') { 43 | ctx.send("Admin privileges required", 403); 44 | } 45 | 46 | // Validation Error 47 | try { 48 | const data = ctx.validate(RequestSchema); 49 | } catch (err) { 50 | ctx.send(err.message, 400); 51 | } 52 | 53 | // Simple Not Found 54 | ctx.send("",404); 55 | ``` 56 | 57 | ### Using `throw new Error()` 58 | 59 | For unexpected internal errors: 60 | 61 | ```typescript 62 | try { 63 | const result = await riskyOperation(); 64 | } catch (error) { 65 | console.error("Internal error:", error); 66 | throw new Error("Internal processing error"); 67 | } 68 | ``` 69 | 70 | ## Middleware Error Handling 71 | 72 | The post-handler function in middleware handles errors from route handlers and validation. 73 | 74 | ### Error Handling Structure 75 | 76 | ```typescript 77 | return (ctx, err?: Error) => { 78 | if (err) { 79 | // 1. Log error 80 | console.error({ 81 | message: `Request Error: ${err.message}`, 82 | stack: err.stack, 83 | requestId: ctx.get("X-Request-ID"), 84 | url: ctx.request.url, 85 | method: ctx.request.method, 86 | }); 87 | // 2. Set status code 88 | ctx.code = ctx.code >= 400 ? ctx.code : 500; 89 | // 3. Format response 90 | const errorMessage = (ctx.code >= 500 && process.env.NODE_ENV === 'production') 91 | ? "Internal Server Error" 92 | : err.message || "An unexpected error occurred"; 93 | const errorResponse = { 94 | error: { 95 | message: errorMessage, 96 | code: ctx.code, 97 | requestId: ctx.get("X-Request-ID"), 98 | }, 99 | timestamp: new Date().toISOString(), 100 | }; 101 | // 4. Send response 102 | ctx.set("Content-Type", "application/json"); 103 | ctx.send(errorResponse); 104 | // 5. Stop processing 105 | return; 106 | } 107 | // Handle successful responses 108 | }; 109 | ``` 110 | 111 | ## Error Cases 112 | 113 | - **404 Not Found:** Send 404 response if no error and no response was sent 114 | - **400 Bad Request:** Use `ctx.send(, 400)` for validation errors 115 | - **401 Unauthorized:** Use `ctx.send(, 401)` with `WWW-Authenticate` header 116 | - **403 Forbidden:** Use `ctx.send(, 403)` for permission issues 117 | - **500 Internal Error:** Handle unexpected errors with generic messages 118 | 119 | ## Best Practices 120 | 121 | - **Centralize:** Implement error handling in global middleware 122 | - **Standardize:** Use consistent JSON structure for error responses 123 | - **Log Effectively:** Include request context in error logs 124 | - **Secure:** Never leak sensitive information in production 125 | - **Use `ctx.send(error, )`:** Prefer it for HTTP-specific errors 126 | 127 | ## Next Steps 128 | 129 | - Review the [Middleware](./middleware.html) documentation 130 | - Learn about the [Context (`ctx`) Object](./context.html) methods 131 | 132 | 133 | -------------------------------------------------------------------------------- /docs/docs/docs/installation.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Installation 5 | 6 | Hello welcome to jetpath. 7 | 8 | 9 | Get ready to build universal JavaScript backends! Installing Jetpath is straightforward and adapts to your preferred runtime environment: Node.js, Deno, or Bun. 10 | 11 | --- 12 | 13 | ## Prerequisites 14 | 15 | Before you begin, ensure you have the following installed: 16 | 17 | 1.**A JavaScript Runtime:** 18 | 19 | **Node.js:** Version 18.x or later recommended. ([Download Node.js](https://nodejs.org/)) 20 | 21 | **Deno:** Version 1.30 or later recommended. ([Install Deno](https://deno.land/manual/getting_started/installation)) 22 | 23 | **Bun:** Version 1.0 or later recommended. ([Install Bun](https://bun.sh/docs/installation)) 24 | 25 | 2.**TypeScript:** Jetpath is built with TypeScript and provides first-class typing support. While you can use it with JavaScript, 26 | 27 | TypeScript is highly recommended for the best experience. 28 | 29 | ```bash 30 | npm install -g typescript 31 | ``` 32 | 33 | --- 34 | 35 | ## Installing Jetpath: 36 | 37 | ```bash 38 | # Create your project folder 39 | mkdir my-api 40 | 41 | # Navigate to your project folder 42 | cd my-api 43 | 44 | # Initialize a new Node.js project 45 | npm init -y 46 | 47 | 48 | 49 | 50 | # Choose the installation method corresponding to your primary development runtime. 51 | 52 | # Using npm 53 | npm install jetpath 54 | 55 | # Using yarn 56 | yarn add jetpath 57 | 58 | # Using pnpm 59 | pnpm add jetpath 60 | ```` 61 | 62 | 63 | ----- 64 | 65 | ## Project Setup 66 | 67 | Regardless of the runtime, a basic project structure and TypeScript configuration are recommended. 68 | 69 | **1. Folder Structure:** 70 | 71 | A common structure looks like this: 72 | 73 | ``` 74 | your-project/ 75 | ├── src/ # Your Jetpath route handlers (.jet.ts files) 76 | │ └── users.jet.ts # where you defined functions for users 77 | │ └── products.jet.ts # where you defined functions for products 78 | │ └── auth.jet.ts # where you defined functions for authentication 79 | │ └── carts.jet.ts # where you defined functions for carts 80 | │ └── users.jet.ts # where you defined functions for users 81 | ├── server.jet.ts # Your main server entry point (initializes Jetpath) 82 | ├── node_modules/ # (Node.js/Bun) 83 | ├── package.json # (Node.js/Bun) 84 | └── tsconfig.json # TypeScript configuration 85 | ``` 86 | 87 | 88 | 89 | ## Next Steps 90 | 91 | * Quick Start: Build your first simple API following the [**Quick Start**](./quickstart.html) guide. 92 | * Core Concepts: Dive deeper into how Jetpath works by reading the [**Core Concepts**](./routing.html). 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /docs/docs/docs/introduction.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Introduction to Jetpath 4 | 5 | **Write Once, Run Cross-runtime** 6 | 7 | Welcome to Jetpath! 🚀 If you're a JavaScript/TypeScript developer looking to build modern APIs, you're in for a treat. Jetpath is more than just another framework - it's your new best friend in backend development, designed to make your life easier while giving you unprecedented flexibility. 8 | 9 | ## What Makes Jetpath Special? 10 | 11 | Jetpath isn't just another framework - it's a new way of thinking about backend development. Here's why developers love it: 12 | 13 | - **Write Once, Run Cross-runtime:** Your code runs seamlessly on Node.js, Deno, or Bun. No more worrying about runtime lock-in! 14 | - **Zero-Config Magic:** Create endpoints by simply writing functions. No need to wrestle with complex configuration files. 15 | - **TypeScript First:** Built with TypeScript from the ground up, giving you the best of both worlds: type safety and JavaScript flexibility. 16 | - **Joy:** We've eliminated all the boring parts of backend development so you can focus on what matters - building amazing features. 17 | 18 | ## How It Works 19 | 20 | Jetpath uses smart conventions and naming-based routing. Instead of writing route definitions, you just: 21 | 22 | 1\. Create `.jet.ts` or `.jet.js` files in your `src` directory 23 | 24 | 2\. Export functions with intuitive names like `METHOD_optionalPathSegment` 25 | 26 | 3\. Let Jetpath handle the rest 27 | 28 | For example, create `src/users.jet.ts` with: 29 | ```typescript 30 | // This becomes GET /users 31 | export const GET_users: JetRoute = (ctx) => { 32 | ctx.send({ message: "I am super fast and runs on any runtime!" }); 33 | }; 34 | ``` 35 | 36 | It's that simple! 37 | 38 | ## Why Developers Love Jetpath 39 | 40 | Here's what makes Jetpath stand out: 41 | 42 | - 🚀 **Blazing Fast Development:** No more configuration files or complex setup. Just write your code and let Jetpath handle the rest. 43 | - 📚 **Automatic API Docs:** Define your validation schemas once, and get beautiful, interactive API documentation that stays in sync with your code. 44 | - 🔒 **Type Safety:** Built with TypeScript first, giving you the best of both worlds: type safety and JavaScript flexibility. 45 | - 🧩 **Unlimited Extensibility:** Extend Jetpath with official plugins or create your own. The framework is designed to grow with your needs. 46 | - 💡 **Smart Conventions:** Instead of fighting with the framework, Jetpath works the way you think. Function naming conventions means your code structure matches your API structure. 47 | 48 | ## Jetpath is perfect for: 49 | 50 | - **Every Developer & Teams:** Build projects faster without sacrificing quality 51 | - **Startups:** Rapidly iterate on your API while maintaining type safety 52 | - **Enterprise Teams:** Build maintainable, scalable APIs that work across multiple runtimes 53 | - **Full-Stack Developers:** Finally have a backend framework that matches the joy of frontend development 54 | - **Anyone Who Hates Boilerplate:** Jetpath handles the boring stuff so you can focus on what matters 55 | - **Anyone Who Loves:** Developer experience, code maintainability, and runtime flexibility 56 | 57 | ## Ready to Get Started? 58 | 59 | Dive into Jetpath and experience the joy of modern backend development: 60 | 61 | - **Quick Start:** Follow our [Quick Start](./quickstart.html) guide to build your first API in minutes 62 | - **Core Concepts:** Explore the [Core Concepts](./routing.html) to understand how everything works together 63 | 64 | Join the [Discord community](https://discord.codedynasty.dev), 65 | 66 | 67 | ## Who is Jetpath For? 68 | 69 | Jetpath is an excellent choice for: 70 | 71 | - **Startups and Teams:** Needing to build and iterate quickly without sacrificing structure or type safety 72 | - **Multi-Platform Deployments:** Developers targeting or potentially migrating between Node.js, Deno, and Bun environments 73 | - **API-First Development:** Projects where clear, automatically generated API documentation is essential 74 | - **Full-Stack Developers:** Looking for a productive and modern JavaScript/TypeScript backend framework 75 | - **Anyone valuing:** Developer experience, code maintainability, and runtime flexibility 76 | 77 | ## Discord? 78 | 79 | Join the growing [community of developers](https://discord.codedynasty.dev) who've discovered that building APIs can be both powerful and fun! 🚀 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /docs/docs/docs/middleware.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Middleware 4 | 5 | Middleware functions are a fundamental part of building applications with Jetpath. They allow you to implement cross-cutting concerns like logging, authentication, and error handling in a clean, reusable way. 6 | 7 | ## What is Middleware? 8 | 9 | Middleware are functions that sit in the middle of the request-response cycle. They can: 10 | 11 | - Execute any code 12 | - Modify request and response objects (`ctx`) 13 | - End the request-response cycle early 14 | - Handle errors 15 | - Process responses after route handlers 16 | 17 | ## Defining Middleware 18 | 19 | In Jetpath, middleware is defined by exporting a special function named `MIDDLEWARE_` from a `.jet.ts` file. 20 | 21 | ```typescript 22 | 23 | import { type JetMiddleware } from "jetpath"; 24 | 25 | export const MIDDLEWARE_: JetMiddleware = (ctx) => { 26 | // Pre-handler: Initial request processing 27 | console.log("Start: ", ctx.request.url); 28 | const startTime = Date.now(); 29 | // Post-handler: Response processing 30 | return (ctx, err) => { 31 | const duration = Date.now() - startTime; 32 | // Error handling 33 | if (err) { 34 | ctx.code = ctx.code >= 400 ? ctx.code : 500; 35 | ctx.send({ 36 | error: { 37 | message: ctx.code < 500 ? err.message : "Internal Server Error", 38 | code: ctx.code, 39 | }, 40 | }); 41 | return; 42 | } 43 | console.log("End: ", ctx.request.url, duration + "ms"); 44 | }; 45 | }; 46 | 47 | ``` 48 | 49 | ### Key Concepts 50 | 51 | 1. **Middleware Structure** 52 | - Pre-handler: Runs before route handler 53 | - Post-handler: Runs after route handler 54 | - Error handling: Built into post-handler 55 | - Response handling: Required for all cases 56 | 57 | 2. **Execution Flow** 58 | - Request → Pre-handler → Route Handler → Post-handler 59 | - Multiple middleware: Pre-handler → Pre-handler → ... → Route Handler → ... → Post-handler → Post-handler 60 | 61 | 3. **Response Handling** 62 | - Always handle responses in post-handler 63 | - Handle errors and 404s appropriately 64 | 65 | 4. **Error Handling** 66 | - Use `err` parameter in post-handler 67 | - Set appropriate status codes 68 | - Send standardized error responses 69 | - Return after sending response 70 | 71 | ## Scoping Middleware 72 | 73 | Middleware can be scoped to apply globally or to specific parts of your API: 74 | 75 | ## Common Use Cases 76 | 77 | - **Logging:** Request/response logging 78 | - **Authentication:** Token verification 79 | - **Authorization:** Role-based access control 80 | - **Response Formatting:** Standard headers and response structure 81 | - **Error Handling:** Centralized error management 82 | - **Rate Limiting:** Request rate control 83 | 84 | ## Error Handling 85 | 86 | Best practices for error handling in middleware: 87 | 88 | 1. Check for errors in the `err` parameter 89 | 2. Log error details 90 | 3. Set appropriate HTTP status code 91 | 4. Send standardized error response 92 | 5. Return after sending response 93 | 94 | ## Best Practices 95 | 96 | - Keep middleware focused on single concerns 97 | - Be mindful of execution order 98 | - Handle errors properly 99 | - Avoid heavy computations in global middleware 100 | 101 | ## Next Steps 102 | 103 | - Learn about the [Context (`ctx`) Object](./context.html) 104 | - Understand the complete [Request Lifecycle](./request-lifecycle.html) 105 | - Review [Error Handling](./error-handling.html) strategies 106 | 107 | 108 | -------------------------------------------------------------------------------- /docs/docs/docs/plugin.md: -------------------------------------------------------------------------------- 1 | 2 | ## jetpath plugins 3 | 4 | Jetpath plugins are a way to extend the functionalities of Jetpath. 5 | They can be used to add new features to Jetpath or to modify existing ones. 6 | 7 | Plugins can be used to add new routes, middleware, or to modify the request 8 | and response objects or even convert to serverless runtime. 9 | 10 | ### Creating a plugin 11 | 12 | ```ts 13 | import { JetPlugin } from "jetpath"; 14 | 15 | export const plugin = new JetPlugin{ 16 | name: "plugin", 17 | version: "1.0.0", 18 | executor({ config }) { 19 | return { 20 | greet_world() { 21 | return { 22 | body: "Hello world", 23 | }; 24 | }, 25 | }; 26 | }, 27 | }); 28 | 29 | ``` -------------------------------------------------------------------------------- /docs/docs/docs/plugins.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Extending Jetpath: Plugins 4 | 5 | Plugins are the primary way to extend Jetpath's core functionality, promote code reuse, and encapsulate complex or shared logic, such as authentication, database interactions, file handling, logging, or connections to third-party services. 6 | 7 | --- 8 | 9 | ## What are Plugins? 10 | 11 | Think of plugins as self-contained modules that can: 12 | 13 | 1. **Initialize Resources:** Set up database connections, configure API clients, read configuration, etc., when the application starts. 14 | 2. **Expose Functionality:** Add new methods and properties to the `ctx.plugins` object, making them easily accessible within your middleware and route handlers. 15 | 3. **Manage Dependencies:** Encapsulate dependencies needed by the plugin's functionality (as discussed in [Dependency Injection](./dependency-injection.md)). 16 | 17 | --- 18 | 19 | ## Using Plugins 20 | 21 | Integrating existing plugins (whether official Jetpath plugins or community-created ones) is straightforward. 22 | 23 | ### 1. Installation 24 | 25 | Install the plugin package using your preferred package manager: 26 | 27 | ```bash 28 | # Example installing an official file upload plugin 29 | npm install @jetpath/plugin-busboy 30 | # or 31 | bun add @jetpath/plugin-busboy 32 | # or add via import map/URL for Deno 33 | ```` 34 | 35 | ### 2\. Registration 36 | 37 | Instantiate the plugin (if necessary, passing configuration options) and register it with your Jetpath application instance using `app.use()`. Registration typically happens in your main server file (`server.ts`). 38 | 39 | ```typescript 40 | // server.ts 41 | import { Jetpath } from "jetpath"; 42 | // Assuming jetbusboy is the exported plugin factory/instance 43 | import { jetbusboy } from "@jetpath/plugin-busboy"; 44 | import { createAuthPlugin } from "./plugins/authPlugin"; // Your custom auth plugin 45 | 46 | const app = new Jetpath({ source: "./src" }); 47 | 48 | // Instantiate and register plugins 49 | // Official plugin for multipart/form-data handling 50 | app.use(jetbusboy); 51 | 52 | // Custom authentication plugin (example) 53 | const authPlugin = createAuthPlugin({ /* options like JWT secret */ }); 54 | app.use(authPlugin); 55 | 56 | app.listen(); 57 | ``` 58 | 59 | *[cite: Registration pattern shown in tests/app.jet.ts]* 60 | 61 | **Important:** Plugins are generally executed/initialized in the order they are registered with `app.use()`. 62 | 63 | ### 3\. Accessing Plugin Functionality 64 | 65 | Once registered, the methods and properties returned by the plugin's `executor` function become available on the `ctx.plugins` object within middleware and route handlers. 66 | 67 | ```typescript 68 | // In a route handler or middleware 69 | import type { JetRoute, JetMiddleware } from "jetpath"; 70 | // Import types exposed by plugins if available 71 | import type { JetBusBoyType } from "@jetpath/plugin-busboy"; 72 | import type { AuthPluginAPI } from "./plugins/authPlugin"; 73 | 74 | // Use generics to type ctx.plugins 75 | type HandlerPlugins = [JetBusBoyType, AuthPluginAPI]; 76 | 77 | export const POST_upload: JetRoute<{}, HandlerPlugins> = async (ctx) => { 78 | // Access file upload functionality from jetbusboy plugin 79 | const formData = await ctx.plugins.formData(ctx); 80 | const image = formData.image; 81 | // ... process image ... 82 | 83 | ctx.send({ message: "Upload processed" }); 84 | }; 85 | 86 | export const GET_profile: JetRoute<{}, HandlerPlugins> = (ctx) => { 87 | // Access auth functionality from authPlugin 88 | const authResult = ctx.plugins.verifyAuth(ctx); // Example method name 89 | if (!authResult.authenticated) { 90 | ctx.send("Not authenticated", 402); 91 | } 92 | ctx.send({ user: authResult.user }); 93 | }; 94 | ``` 95 | 96 | *[cite: Usage pattern `ctx.plugins.methodName()` shown in tests/app.jet.ts]* 97 | 98 | ----- 99 | 100 | ## Creating Plugins 101 | 102 | Creating your own plugins allows you to structure reusable logic cleanly. 103 | 104 | ### The `executor` Function 105 | 106 | * **Purpose:** This function is the core of your plugin. It's executed when `app.use(yourPluginInstance)` is called. 107 | * **Initialization:** Use the `executor` to perform any setup required by your plugin (e.g., establish database connections, initialize SDKs, read configuration). It can be `async` if needed. 108 | * **Return Value:** The `executor` **must return an object**. The properties and methods of this returned object are merged into the `ctx.plugins` object, forming the public API of your plugin. 109 | * **Dependency Scope:** Variables defined *outside* the returned object but *within* the `executor`'s scope (or the factory function's scope, like `prefix` and `dbClient` in the examples) act as private state or encapsulated dependencies for your plugin's public methods. 110 | 111 | ### Example: Simplified Auth Plugin Structure 112 | 113 | This mirrors the `authPlugin` structure seen in `tests/app.jet.ts`. 114 | 115 | ```typescript 116 | import { JetPlugin } from "jetpath"; 117 | import type { JetContext } from "jetpath"; 118 | 119 | // Define the API exposed by this plugin 120 | export interface AuthPluginAPI { 121 | verifyAuth: (ctx: JetContext) => { authenticated: boolean; user?: any; message?: string }; 122 | isAdmin: (ctx: JetContext) => boolean; 123 | } 124 | 125 | // Define configuration options 126 | interface AuthPluginOptions { 127 | jwtSecret: string; 128 | adminApiKey: string; 129 | } 130 | 131 | export function createAuthPlugin(options: AuthPluginOptions): JetPlugin { 132 | // Dependencies are configured here and accessible within the executor's returned methods 133 | const JWT_SECRET = options.jwtSecret; 134 | const ADMIN_API_KEY = options.adminApiKey; 135 | // In-memory store or DB connection could be initialized here 136 | 137 | return ({ 138 | executor(): AuthPluginAPI { 139 | // Return the methods that handlers will call via ctx.plugins 140 | return { 141 | 142 | 143 | verifyAuth(this: JetContext) { 144 | // ? the this here is the ctx of the api request, hence you have access to the entire context; 145 | const authHeader = this.get("authorization"); 146 | // ... logic to validate token using JWT_SECRET ... 147 | if (/* valid token */) { 148 | // const user = findUserFromToken(...); 149 | return { authenticated: true, user: { id: '...', role: '...' } }; 150 | } 151 | return { authenticated: false, message: "Invalid token" }; 152 | }, 153 | 154 | isAdmin(this: JetContext) { 155 | if (this.get("x-admin-key") === ADMIN_API_KEY) { 156 | return true; 157 | } 158 | const auth = this.verifyAuth(ctx); // Can call other plugin methods 159 | return auth.authenticated && auth.user?.role === "admin"; 160 | } 161 | }; 162 | } 163 | }); 164 | } 165 | ``` 166 | 167 | *[cite: Based on `authPlugin` structure in tests/app.jet.ts]* 168 | 169 | ----- 170 | 171 | ## Plugin Lifecycle 172 | 173 | * **Instantiation:** You create an instance of your plugin, potentially passing configuration options. 174 | * **Registration:** You call `app.use(pluginInstance)`. 175 | * **Execution:** The plugin's `executor` function runs during the `app.use()` call. Any asynchronous operations within the `executor` should complete before the server starts fully listening or handling requests (depending on Jetpath's internal handling, usually `app.listen` awaits plugin initialization implicitly or explicitly). 176 | * **Runtime:** The methods returned by the `executor` are available on `ctx.plugins` for every incoming request handled after the plugin was registered. 177 | 178 | ----- 179 | 180 | ## Best Practices 181 | 182 | * **Single Responsibility:** Design plugins to handle a specific concern (authentication, database access, specific API client). 183 | * **Clear API:** Define a clear and well-typed interface for the functionality your plugin exposes. 184 | * **Configuration:** Allow configuration via options passed during instantiation rather than relying solely on global environment variables within the plugin. 185 | * **Asynchronous Initialization:** Handle connections and other async setup correctly within the `executor` using `async/await`. 186 | * **Documentation:** Document your plugin's configuration options and the methods it provides on `ctx.plugins`. 187 | 188 | ----- 189 | 190 | ## Next Steps 191 | 192 | * Understand how plugin methods are accessed via the [**Context (`ctx`) Object**](./context.html). 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /docs/docs/docs/quickstart.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Quick Start Guide 5 | 6 | Let's build your first Jetpath application! This guide will walk you through creating a simple API server that returns a welcome message. 7 | 8 | ## Prerequisites 9 | 10 | Before you start, make sure you have: 11 | 12 | - A compatible JavaScript runtime (Node.js v18+, Deno v1.30+, or Bun v1.0+) 13 | 14 | ## Create a New Project 15 | 16 | ```bash 17 | npx jetpath my-api 18 | ``` 19 | 20 | Your project structure will look like this: 21 | 22 | ``` 23 | my-api/ 24 | ├── src/ 25 | │ ├── app/ 26 | │ │ ├── auth.jet.ts 27 | │ │ ├── carts.jet.ts 28 | │ │ ├── products.jet.ts 29 | │ │ └── users.jet.ts 30 | │ ├── db/ 31 | │ │ ├── schema.ts 32 | │ │ ├── interfaces.ts 33 | │ │ └── index.ts 34 | │ └── site/ 35 | │ ├── index.html 36 | │ ├── about.html 37 | │ └── contact.html 38 | ├── package.json 39 | ├── .dockerignore 40 | ├── .gitignore 41 | ├── Dockerfile 42 | ├── README.md 43 | ├── fly.toml 44 | └── tsconfig.json 45 | ``` 46 | 47 | ## Install Dependencies 48 | 49 | ```bash 50 | cd my-api 51 | npm install # or yarn install or pnpm install or bun install 52 | ``` 53 | 54 | ## Create Your First Route 55 | 56 | Create a file named `users.jet.ts` in the `src` directory: 57 | 58 | ```typescript 59 | // src/users.jet.ts 60 | import { type JetRoute, use } from "jetpath"; 61 | 62 | /** 63 | * Handles GET requests to the root path ('/') 64 | */ 65 | export const GET_users: JetRoute = (ctx) => { 66 | ctx.send({ 67 | message: "Welcome to your first Jetpath API!", 68 | status: "ok" 69 | }); 70 | }; 71 | 72 | use(GET_users).title("Returns a welcome message and API status."); 73 | ``` 74 | 75 | ## Create the Server Entry Point 76 | 77 | Create a file named `server.ts` in your project root: 78 | 79 | ```typescript 80 | // server.ts 81 | import { Jetpath } from "jetpath"; 82 | 83 | const app = new Jetpath({ 84 | source: "./src", 85 | port: 3000, 86 | apiDoc: { 87 | name: "My First Jetpath API", 88 | info: "This is the documentation for the Quick Start API.", 89 | color: "#7e57c2", 90 | display: "UI" 91 | } 92 | }); 93 | 94 | app.listen(); 95 | ``` 96 | 97 | ## Run Your Server 98 | 99 | ```bash 100 | npx run dev 101 | ``` 102 | 103 | ## Verify It Works 104 | 105 | 1. Open your browser and navigate to `http://localhost:3000` to see the JSON response 106 | 2. Visit `http://localhost:3000/api-docs` to explore the interactive API documentation 107 | 108 | **Congratulations! You've successfully created and run your first Jetpath application!** 109 | 110 | ## Next Steps 111 | 112 | - Learn about [Routing](./routing.md), the [Context Object](./context.md), [Validation](./validation.md), and [Middleware](./middleware.md) 113 | - Build more complex features by following the practical [Guides](./guides/crud-api.md) 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/docs/docs/request-lifecycle.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Request Lifecycle 4 | 5 | Understanding Jetpath's request lifecycle is essential for building efficient and maintainable applications. This document outlines the complete journey of a request from request to response. 6 | 7 | Request Lifecycle 8 | 9 | ## 1. Request 10 | 11 | When a request is received: 12 | 13 | - The request path is matched against registered routes 14 | - A `Context` instance is retrieved from the pool for the request 15 | - Request metadata is extracted (method, headers, path) 16 | - Query parameters are normalized 17 | 18 | ## 2. Pre-Handler Middleware 19 | 20 | Before reaching the route handler, the request passes through pre-handler middleware: 21 | 22 | - Executed before the route handler 23 | - Can modify the request or response 24 | - Used for authentication, validation, rate limiting, and logging 25 | 26 | ## 3. Route Handler Execution 27 | 28 | Once the request reaches the handler: 29 | 30 | - The handler executes the business logic 31 | - Processes request data 32 | - Generates the response 33 | 34 | ## 4. Post-Processing 35 | 36 | ### Post-Handler Middleware 37 | 38 | After the handler executes, the response passes through post-handler middleware: 39 | 40 | - Modifies the response as needed 41 | - Performs final processing 42 | - Sends the response to the client 43 | 44 | ## Error Handling 45 | 46 | Error handling is integrated throughout the lifecycle: 47 | - Catches and handles errors at each stage 48 | - Ensures consistent error responses 49 | - Provides error recovery mechanisms 50 | -------------------------------------------------------------------------------- /docs/docs/docs/resources/resources.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Resources 4 | 5 | ### Application Types 6 | 7 | For detailed information, see: 8 | - [Context API Reference](/docs/context.html) 9 | - [Plugin Documentation](/docs/plugins.html) 10 | - [Routing Guide](/docs/routing.html) 11 | - [Middleware Guide](/docs/middleware.html) 12 | 13 | -------------------------------------------------------------------------------- /docs/docs/docs/routing.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Core Concepts 1: Routing 5 | 6 | Routing in Jetpath is designed to be intuitive, relying on **convention over configuration** through a **function naming convention**. Your exported function names directly determine your API endpoints. 7 | 8 | ## Key Concepts 9 | 10 | ### 1. Source Directory 11 | 12 | When you initialize Jetpath, specify a `source` directory for your `.jet.ts` route handler files: 13 | 14 | ```typescript 15 | // index.jet.ts 16 | import { Jetpath } from "jetpath"; 17 | 18 | const app = new Jetpath({ 19 | source: "./src" 20 | }); 21 | 22 | app.listen(); 23 | ``` 24 | 25 | ### 2. Handler Files (`.jet.ts`) 26 | 27 | Files ending with `.jet.ts` are scanned for exported functions that define route handlers. Other `.ts` or `.js` files are ignored for routing but can be imported by your handlers. 28 | 29 | ### 3. Export Naming Convention 30 | 31 | The core convention lies in the names of the exported functions: 32 | 33 | - **Format:** `METHOD_optionalPathSegment` 34 | - **`METHOD`:** HTTP method prefix (uppercase only): 35 | - `GET_` 36 | - `POST_` 37 | - `PUT_` 38 | - `DELETE_` 39 | - `PATCH_` 40 | - `HEAD_` 41 | 42 | **Examples: In `src/pets.jet.ts`:** 43 | 44 | ```typescript 45 | // GET /pets 46 | export const GET_pets: JetRoute = (ctx) => { ... } 47 | 48 | // POST /pets 49 | export const POST_pets: JetRoute = async (ctx) => { ... } 50 | 51 | // GET /pets/search 52 | 53 | export const GET_pets_search: JetRoute = (ctx) => { ... } 54 | ``` 55 | 56 | ### 4. Path Parameters (`$paramName`) 57 | 58 | Capture dynamic segments using a `$` prefix in filenames or export names: 59 | 60 | ```typescript 61 | // Maps to: GET /pets/by/:id 62 | export const GET_pets_by$id: JetRoute = (ctx) => { 63 | const petId = ctx.params.id; 64 | // ... 65 | }; 66 | 67 | 68 | // Maps to: GET /pets/petBy/:id/:slug 69 | export const GET_pets_petBy$id$slug: JetRoute = (ctx) => { 70 | const petId = ctx.params.id; 71 | const slug = ctx.params.slug; 72 | // ... 73 | }; 74 | ``` 75 | 76 | ### 5. Catch-all Routes (`$0`) 77 | 78 | Match multiple path segments using `$0 79 | `: 80 | 81 | ```typescript 82 | // Maps to GET /* 83 | export const GET_$0: JetRoute = (ctx) => { 84 | const filePath = ctx.params.$0; // e.g., "file.txt" from 85 | ctx.sendStream(filePath, { 86 | folder: "./files", 87 | }); 88 | }; 89 | 90 | 91 | ``` 92 | 93 | ### 6. WebSocket Routes (`WS`) via `ctx.upgrade()` 94 | 95 | ```typescript 96 | // Maps to ws://your-host/live 97 | export const GET_live: JetRoute = (ctx) => { 98 | ctx.upgrade(); 99 | const conn = ctx.connection!; 100 | conn.addEventListener("open", (socket) => { /* ... */ }); 101 | conn.addEventListener("message", (socket, event) => { /* ... */ }); 102 | // ... 103 | }; 104 | ``` 105 | 106 | ## Route Precedence 107 | 108 | Jetpath follows standard routing precedence: 109 | Static routes (e.g., `/pets/search`) are matched before dynamic routes (`/pets/:id`) 110 | 111 | ## Next Steps 112 | 113 | - Learn how the [Context (`ctx`) Object](./context.html) provides access to request/response data 114 | - Understand the full [Request Lifecycle](./request-lifecycle.html), including middleware execution 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /docs/docs/home-links/tutorial.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Building APIs with Jetpath: A Framework Creator's Perspective 4 | 5 | As the creator of Jetpath, I'm excited to share the story behind this framework and how it can revolutionize the way you build APIs. Jetpath was born from my frustration with existing frameworks and a vision for a simpler, more declarative approach to API development. 6 | 7 | ## The Journey to Jetpath 8 | 9 | Before creating Jetpath, I spent years working with various frameworks, each with their own set of challenges: 10 | 11 | - Too much boilerplate code 12 | - Complex configuration 13 | - Manual type definitions 14 | - Tedious documentation maintenance 15 | - Runtime-specific limitations 16 | 17 | I wanted to create something different - a framework that would make API development enjoyable again. 18 | 19 | ## The Vision Behind Jetpath 20 | 21 | When I designed Jetpath, I had several key principles in mind: 22 | 23 | 1. **Simplicity First** - No configuration needed to get started 24 | 2. **Type Safety** - Built-in TypeScript support with zero configuration 25 | 3. **Declarative Approach** - Routes as simple exports instead of complex registrations 26 | 4. **Developer Experience** - Automatic documentation and intuitive API design 27 | 5. **Cross-Platform** - Works seamlessly across Node.js, Deno, and Bun 28 | 29 | ## Building the Pet Shop API 30 | 31 | To demonstrate Jetpath's capabilities, let's build a simple pet shop API. This example showcases the core principles that guided Jetpath's development. 32 | 33 | ### Step 1: Setting Up the Project 34 | 35 | First, I created a new directory and initialized the project: 36 | 37 | ```bash 38 | mkdir petshop-api 39 | cd petshop-api 40 | npm init -y 41 | npm install jetpath 42 | ``` 43 | 44 | ### Step 2: Creating the Application 45 | 46 | I started with a simple `app.jet.ts`: 47 | 48 | ```typescript 49 | import { Jetpath } from 'jetpath'; 50 | 51 | // Initialize Jetpath with configuration 52 | const app = new Jetpath({ 53 | port: 3000, 54 | apiDoc: { 55 | display: "UI", 56 | name: "Pet Shop API", 57 | color: "#7e57c2" 58 | } 59 | }); 60 | 61 | // Start the server 62 | app.listen(); 63 | ``` 64 | 65 | ### Step 3: Real-World Challenges 66 | 67 | As the API grew, I encountered some common challenges: 68 | 69 | 1. **Type Safety** - Ensuring consistent data types across endpoints 70 | 2. **Error Handling** - Managing different types of errors gracefully 71 | 3. **Documentation** - Keeping API docs up to date 72 | 73 | Jetpath solved these problems beautifully. The type inference system made it easy to maintain consistency, and the automatic documentation saved me hours of work. 74 | 75 | ### Step 4: Building the Pet Management System 76 | 77 | Here's how I implemented the core functionality: 78 | 79 | ```typescript 80 | // In-memory storage for our pets 81 | const pets: Record = {}; 82 | let nextId = 1; 83 | 84 | // Get all pets 85 | export const GET_pets = (ctx) => { 86 | // Tip: Use ctx.query to handle pagination 87 | const page = ctx.query.page || 1; 88 | const limit = ctx.query.limit || 10; 89 | 90 | const petsList = Object.values(pets); 91 | const startIndex = (page - 1) * limit; 92 | const endIndex = startIndex + limit; 93 | 94 | ctx.send({ 95 | pets: petsList.slice(startIndex, endIndex), 96 | total: petsList.length, 97 | page, 98 | limit 99 | }); 100 | }; 101 | 102 | // Create a new pet with validation 103 | export const POST_pets: JetRoute< 104 | { body: { name: string; species: string; age: number } } 105 | > = async (ctx) => { 106 | await ctx.parse(); 107 | const { name, species, age } = ctx.body; 108 | 109 | // Create the pet 110 | const pet = { 111 | id: nextId++, 112 | name, 113 | species, 114 | age 115 | }; 116 | 117 | // Save to our in-memory storage 118 | pets[pet.id] = pet; 119 | 120 | // Send success response 121 | ctx.send(pet, 201); 122 | }; 123 | 124 | // Define our pet schema 125 | use(POST_pets).body((t)=>{ 126 | return { 127 | name: t.string().min(1).max(50), 128 | species: t.string().min(1).max(50), 129 | age: t.number().min(0).max(30) 130 | } 131 | }); 132 | ``` 133 | 134 | ## Advanced Features I Love 135 | 136 | ### 1. Validation Made Easy 137 | 138 | The validation system in Jetpath is a game-changer. Here's how I use it: 139 | 140 | ```typescript 141 | // Define our pet schema 142 | use(POST_pets).body((t)=>{ 143 | // Real-world validation rules 144 | return { 145 | name: t.string().min(1).max(50) 146 | .withMessage('Name must be between 1 and 50 characters'), 147 | species: t.string().min(1).max(50) 148 | .withMessage('Species must be between 1 and 50 characters'), 149 | age: t.number().min(0).max(30) 150 | .withMessage('Age must be between 0 and 30') 151 | } 152 | }); 153 | ``` 154 | 155 | ### 2. Middleware for Cross-Cutting Concerns 156 | 157 | I implemented middleware to handle logging and error handling: 158 | 159 | ```typescript 160 | export const MIDDLEWARE_ = (ctx) => { 161 | // Log every request with detailed information 162 | console.log(`[${new Date().toISOString()}] ${ctx.request.method} ${ctx.request.url} - ${ctx.request.headers['user-agent']}`); 163 | 164 | // Add useful headers to responses 165 | ctx.set('X-Request-ID', Date.now().toString()); 166 | ctx.set('X-Response-Time', `${Date.now() - ctx.request.time}ms`); 167 | 168 | // Handle errors with proper logging 169 | return (ctx, err) => { 170 | if (err) { 171 | console.error('Error:', { 172 | timestamp: new Date().toISOString(), 173 | method: ctx.request.method, 174 | url: ctx.request.url, 175 | error: err.message, 176 | stack: err.stack 177 | }); 178 | return ctx.send('Internal server error'); 179 | } 180 | }; 181 | }; 182 | ``` 183 | 184 | ## Best Practices I've Learned 185 | 186 | Here are some practical tips I've picked up along the way: 187 | 188 | 1. **Start Simple** 189 | - Begin with basic CRUD operations 190 | - Add complexity gradually 191 | - Keep your code DRY (Don't Repeat Yourself) 192 | 193 | 2. **Validation Tips** 194 | - Always validate input data 195 | - Use descriptive error messages 196 | - Consider edge cases (like empty strings or null values) 197 | 198 | 3. **Error Handling** 199 | - Use `ctx.send(msg, code)` and return; 200 | - Implement global error handling 201 | - Log errors with context 202 | 203 | ## Real-World Use Cases 204 | 205 | I've used Jetpath for several projects: 206 | 207 | 1. **Microservices** - Building small, focused services 208 | 2. **API Gateways** - Routing and transforming requests 209 | 3. **Internal Tools** - Creating admin interfaces and dashboards 210 | 211 | ## Next Steps for Your API 212 | 213 | Now that you've built a basic API, here are some practical suggestions: 214 | 215 | 1. **Add Authentication** 216 | - Implement JWT tokens 217 | - Add role-based access control 218 | - Secure sensitive endpoints 219 | 220 | 2. **Performance Optimization** 221 | - Add caching layers 222 | - Implement rate limiting 223 | - Optimize database queries 224 | 225 | 3. **Monitoring and Logging** 226 | - Add error tracking 227 | - Monitor API performance 228 | - Set up alerting 229 | 230 | 4. **Testing** 231 | - Write unit tests 232 | - Add integration tests 233 | - Implement end-to-end tests 234 | 235 | ## Conclusion 236 | 237 | Jetpath has transformed the way I build APIs. The declarative approach makes development faster, more reliable, and more enjoyable. Whether you're building a small microservice or a large-scale application, Jetpath provides the tools you need to succeed. 238 | 239 | I hope this journey through Jetpath has been helpful. Remember, the key to success is starting simple and building incrementally. Happy coding! 240 | 241 | 242 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | 2 | # PetShop API Sample 3 | 4 | The goal is to make the codebase clearer for beginners by splitting the original monolithic file into logical modules, while preserving all the features and conventions demonstrated in the original sample. 5 | 6 | This structure helps beginners understand how to build a Jetpath application by separating concerns into different files: 7 | 8 | - **Framework Initialization and Configuration:** How the app is set up. 9 | - **Global Middleware:** Logic applied to all requests (logging, error handling). 10 | - **Data Models and Storage:** Where data structures and in-memory data reside. 11 | - **API Routes:** Grouping related API endpoints together. 12 | - **Utilities/Miscellaneous Routes:** Other supporting endpoints. 13 | - **WebSockets:** Handling real-time communication. 14 | - **Plugins:** External functionalities integrated into the framework. 15 | 16 | All features from the original `app.jet.ts` sample are included in this version, including: 17 | 18 | - Convention-over-configuration routing (using exported function names like `GET_pets`, `POST_auth_login`). 19 | - Global middleware (`MIDDLEWARE_`) for logging and error handling. 20 | - In-memory data storage (arrays of pet and review objects). 21 | - Authentication and Authorization checks (using a mock plugin). 22 | - Logging (using a mock plugin). 23 | - Input validation using `use().body()` (including objects, arrays, files). 24 | - Handling various HTTP methods (GET, POST, PUT, DELETE). 25 | - Dynamic routing with path parameters (e.g., `/petBy/:id`). 26 | - Query parameter parsing for filtering, sorting, and pagination. 27 | - **File uploads** (handling multipart/form-data). 28 | - **WebSocket communication** for real-time updates. 29 | - Automatic API Documentation UI (Swagger UI). 30 | - API Documentation Export (JSON, YAML, Markdown). 31 | - Error Handling and Testing routes (`/error`). 32 | - Health Check route (`/health`). 33 | - Serving static/uploaded files (`/serve/*`, `/static/*`). 34 | 35 | ## Code Structure 36 | 37 | The original `app.jet.ts` file has been split into the following directories and files within the `src` folder: 38 | 39 | ``` 40 | 41 | petshop-api/ 42 | ├── src/ 43 | │ ├── data/ 44 | │ │ └── models.ts \# Defines data types and in-memory data arrays (pets, reviews). 45 | │ ├── middleware/ 46 | │ │ └── global.ts \# Contains the global middleware (MIDDLEWARE\_) for logging and error handling. 47 | │ ├── plugins/ \# Directory for external plugins (mock examples) 48 | │ │ ├── auth.ts \# Mock authentication plugin (authenticate, verify, isAdmin). 49 | │ │ └── logging.ts \# Mock logging plugin (info, warn, error). 50 | │ ├── routes/ 51 | │ │ ├── auth.ts \# Authentication routes (e.g., POST /auth/login). 52 | │ │ ├── live.ts \# WebSocket route (GET /live) and broadcasting logic. 53 | │ │ ├── pets.ts \# Pet management routes (CRUD, search, image upload, gallery). 54 | │ │ ├── reviews.ts \# Review management routes (GET, POST, DELETE reviews for pets). 55 | │ │ └── utils.ts \# Utility and miscellaneous routes (/, /stats, /error, /health, /export/docs, /upload, /serve, /static). 56 | │ ├── types.ts \# Defines TypeScript types used across the application (PetType, ReviewType). 57 | │ └── index.ts \# Main application entry point: Jetpath app initialization, configuration, plugin registration, imports. 58 | ├── package.json \# Project dependencies and scripts. 59 | ├── README.md \# This file. 60 | └── pet-shop-api-log.log \# Log file generated by the logger plugin (created on first run). 61 | └── uploads/ \# Directory for uploaded files (create this manually) 62 | ├── pet-images/ \# Directory for uploaded pet images (create this manually) 63 | └── general-files/ \# Directory for general file uploads (create this manually) 64 | └── served-content/ \# Directory for files served via /serve/\* (create this manually) 65 | └── public/ \# Directory for static files served via /static/\* (create this manually) 66 | └── static/ \# Subdirectory for static files (create this manually) 67 | 68 | ```` 69 | 70 | ## Explanation of the Structure 71 | 72 | - **`src/index.ts`**: This is the application's bootstrapper. It sets up the core Jetpath application instance with global configurations (`apiDoc`, `globalHeaders`, `port`, `upgrade`). It initializes the database (in this case, conceptually preparing the in-memory data). It registers external plugins using `app.addPlugin()`. Crucially, it **imports** the other route and middleware files. Jetpath then automatically discovers and registers the exported route handlers (like `GET_pets`, `POST_auth_login`) and global middleware (`MIDDLEWARE_`) based on their naming conventions in these imported files. 73 | - **`src/types.ts`**: Centralizes the TypeScript interface/type definitions (`PetType`, `ReviewType`), promoting code consistency and clarity. 74 | - **`src/data/models.ts`**: Holds the application's data structures (the `PetType` and `ReviewType` definitions again for clarity, although imported from `types.ts`) and the in-memory data arrays (`pets`, `reviews`). In a real-world application, this layer would contain database connection logic and functions to interact with the database, abstracting data access away from the route handlers. 75 | - **`src/middleware/global.ts`**: Contains the single exported `MIDDLEWARE_` function. This function defines logic that runs for **every** incoming request (the pre-handler) and after the request is processed (the post-handler), handling cross-cutting concerns like logging, authentication checks, and error handling as demonstrated in the original sample. Jetpath automatically applies this global middleware due to its name. 76 | - **`src/plugins/`**: This directory represents external plugins. The original sample referenced `auth.ts` and `logging.ts`. These are included here as mock examples showing the expected structure (exporting plugin instances with specific methods like `authenticateUser`, `info`, `error`) that Jetpath's `app.addPlugin()` expects. The actual implementation details of these plugins would be more complex. 77 | - **`src/routes/`**: This directory contains files that group API endpoint handlers by functionality (auth, pets, reviews, live, utils). Each file exports functions following Jetpath's `METHOD_path$param` naming convention (e.g., `GET_pets`, `POST_auth_login`, `PUT_petBy$id`, `GET_live`). 78 | - `use(functionName).body(...)` is chained directly after the function definition to specify input validation for the request body. 79 | - `use(functionName).info(...)` is chained (often after `use().body()`) to provide documentation details for that specific endpoint, which Jetpath uses to generate the API documentation UI and export. 80 | - Route handlers access the request context (`ctx`) to get parameters, body, plugins, state, and send responses. They interact with the data layer (`../../data/models`). 81 | - **File System Directories**: `uploads/pet-images`, `uploads/general-files`, `served-content`, and `public/static` are directories used by the file upload and serving routes. You need to create these manually for those features to work correctly. 82 | 83 | This structure is designed to be easier for a beginner to navigate, understand the separation of concerns, and see how different parts of a Jetpath application fit together while still preserving the framework's unique conventions. 84 | 85 | ## Prerequisites 86 | 87 | * **Node.js** (v18 or higher recommended) or **Bun** (v1.0 or higher recommended). Ensure your chosen runtime is installed and accessible from your terminal. 88 | * A basic understanding of JavaScript or TypeScript. 89 | * Familiarity with fundamental backend and RESTful API concepts. 90 | * The **Jetpath framework** source code or package installed/accessible. You **must** update the `jetpath` dependency path in `package.json` to point to the correct location or package name of the Jetpath framework. 91 | 92 | ## Setup 93 | 94 | 1. **Obtain the Code:** Create the directory structure (`src`, `src/data`, `src/middleware`, `src/plugins`, `src/routes`, `src/routes/data`, `src/routes/middleware`, `src/routes/plugins`, `src/routes/routes`, `src/routes/types`) and files listed above, or clone the repository if this code is hosted in a Git repository: 95 | ```bash 96 | # If cloning from a repo 97 | git clone 98 | cd petshop-api 99 | ``` 100 | *(Replace `` with the actual URL if applicable)* 101 | 102 | 2. **Create Necessary Directories:** Manually create the file system directories used by the file handling routes: 103 | ```bash 104 | mkdir -p uploads/pet-images 105 | mkdir -p uploads/general-files 106 | mkdir -p served-content 107 | mkdir -p public/static 108 | ``` 109 | 110 | 3. **Update the `jetpath` dependency:** 111 | Open the `package.json` file and change the line `"jetpath": "file:../path/to/your/jetpath/dist"` to the correct path relative to your project's root directory where the Jetpath framework's `dist` directory is located, or replace it with the package name if Jetpath is published to an npm registry. 112 | 113 | 4. **Install dependencies:** 114 | Open your terminal in the project's root directory and run the appropriate command for your chosen runtime: 115 | 116 | * **If you will primarily use Node.js:** 117 | ```bash 118 | npm install 119 | ``` 120 | This will install `better-sqlite3` (if needed for SQLite, although this sample uses in-memory arrays), `ts-node` (which allows Node.js to run TypeScript files directly), and `typescript`, plus any other dependencies defined in `package.json` (like placeholders for plugins). 121 | 122 | * **If you will primarily use Bun:** 123 | ```bash 124 | bun install 125 | ``` 126 | Bun will handle the dependencies listed in `package.json`. Bun runs TypeScript files directly, so `ts-node` is not needed. 127 | 128 | ## Running the API 129 | 130 | You can run the API using either Node.js or Bun. The in-memory data will reset each time the server restarts. 131 | 132 | * **Using Node.js:** 133 | ```bash 134 | npm run start:node 135 | ``` 136 | This script uses `ts-node` to execute the `src/index.ts` file with Node.js. 137 | 138 | * **Using Bun:** 139 | ```bash 140 | bun run src/index.ts 141 | # Alternatively, use the npm script alias: 142 | bun run start:bun 143 | ``` 144 | Bun runs the `src/index.ts` file directly, using its native TypeScript capabilities. 145 | 146 | Once running, the server will print messages to the console indicating which port it's listening on (defaulting to 9000, as in the original sample) and the URL where you can access the API documentation. 147 | 148 | ## API Documentation (Swagger UI) 149 | 150 | An interactive API documentation interface (Swagger UI) is automatically generated and available while the server is running. Open your web browser and go to: 151 | 152 | `http://localhost:9000/api-doc` 153 | 154 | *(Note the port 9000 as configured in `src/index.ts`)*. This documentation is created by Jetpath based on the `apiDoc` configuration in `src/index.ts` and the `.info` properties you've added to the route handlers. It provides a convenient way to explore the API's endpoints and their expected parameters and responses. The documentation itself is secured with basic auth (admin/1234) as in the original sample. 155 | 156 | ## Example API Calls and Features 157 | 158 | You can interact with the API using command-line tools like `curl` or graphical clients like Postman or Insomnia. Since the original sample used in-memory data and mock plugins, you'll need to authenticate first to access protected routes (admin/1234). 159 | 160 | **1. Get API Status (GET /):** 161 | 162 | ```bash 163 | curl http://localhost:9000/ 164 | ```` 165 | 166 | *Expect basic API information.* 167 | 168 | **2. Authenticate (POST /auth/login):** 169 | 170 | ```bash 171 | curl -X POST http://localhost:9000/auth/login \ 172 | -H "Content-Type: application/json" \ 173 | -d '{"username": "admin", "password": "1234"}' 174 | ``` 175 | 176 | *Expect a token in the response. You will need this token in the `Authorization: Bearer ` header for protected routes.* 177 | 178 | **3. Get all pets (GET /pets):** 179 | 180 | ```bash 181 | curl http://localhost:9000/pets 182 | # With pagination/filtering example: 183 | # curl "http://localhost:9000/pets?limit=5&species=dog&search=friendly" 184 | ``` 185 | 186 | *Expect a list of pets.* 187 | 188 | **4. Add a new pet (POST /pets - requires Authentication and Admin role):** 189 | 190 | ```bash 191 | curl -X POST http://localhost:9000/pets \ 192 | -H "Content-Type: application/json" \ 193 | -H "Authorization: Bearer " \ 194 | -d '{ 195 | "name": "Buddy", 196 | "species": "Dog", 197 | "breed": "Beagle", 198 | "age": 2, 199 | "gender": "Male", 200 | "color": "Brown and White", 201 | "description": "Energetic and playful Beagle", 202 | "price": 300, 203 | "available": true, 204 | "tags": ["playful", "loyal"] 205 | }' 206 | ``` 207 | 208 | *Replace `` with the token from the login response.* 209 | 210 | **5. Upload a pet image (POST /petImage/:id - requires Authentication and Admin role):** 211 | 212 | ```bash 213 | curl -X POST http://localhost:9000/petImage/pet-1 \ 214 | -H "Authorization: Bearer " \ 215 | -F "image=@/path/to/your/image.jpg" \ 216 | -H "Content-Type: multipart/form-data" 217 | ``` 218 | 219 | *Replace `pet-1` with a pet ID and `/path/to/your/image.jpg` with an image file path.* 220 | 221 | **6. Connect to WebSocket for live updates (GET /live):** 222 | 223 | Use a WebSocket client to connect to `ws://localhost:9000/live`. You should receive messages for certain events (like pet additions, though this might require adding broadcast calls in the route handlers). 224 | 225 | **7. Export API Documentation (GET /export/docs/:format):** 226 | 227 | ```bash 228 | curl http://localhost:9000/export/docs/json 229 | # or yaml, or markdown 230 | ``` 231 | 232 | By exploring these modular files, you should gain a clearer understanding of how the different features of Jetpath demonstrated in the original sample can be organized into a more maintainable structure. 233 | 234 | ## Mock Plugins 235 | 236 | The sample relies on `authPlugin` and `jetLogger`. Simple mock versions are provided in `src/plugins/` to allow the code to run. In a real application, you would use actual plugin implementations. 237 | -------------------------------------------------------------------------------- /example/chat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Modern WebSocket Chat 7 | 8 | 9 | 10 | 248 | 249 | 250 |
251 |
252 |

Jetpath WS Chat

253 |
254 |
255 | Disconnected 256 |
257 |
258 | 259 |
260 |
261 | 262 |
263 |
264 | 265 |
266 | 267 | 268 | 273 |
274 |
275 | 276 | 392 | 393 | 394 | -------------------------------------------------------------------------------- /example/data/models.ts: -------------------------------------------------------------------------------- 1 | import type { PetType, ReviewType } from "../types.js"; // Import types 2 | 3 | /** 4 | * In-memory pet database 5 | * In a production application, this would be replaced with a real database 6 | * Exported for use in route handlers. 7 | */ 8 | export const pets: PetType[] = [ 9 | { 10 | id: "pet-1", 11 | name: "Max", 12 | species: "Dog", 13 | breed: "Golden Retriever", 14 | age: 3, 15 | gender: "Male", 16 | color: "Golden", 17 | description: "Friendly and energetic golden retriever, great with children", 18 | image: "/assets/images/max.jpg", 19 | price: 500, 20 | available: true, 21 | createdAt: "2023-06-15T10:30:00Z", 22 | updatedAt: "2023-06-15T10:30:00Z", 23 | tags: ["friendly", "energetic", "family-friendly"], 24 | health: { 25 | vaccinated: true, 26 | neutered: true, 27 | medicalHistory: ["Regular checkup - 2023-05-01"], 28 | }, 29 | }, 30 | { 31 | id: "pet-2", 32 | name: "Luna", 33 | species: "Cat", 34 | breed: "Siamese", 35 | age: 2, 36 | gender: "Female", 37 | color: "Cream with brown points", 38 | description: 39 | "Elegant Siamese cat with striking blue eyes and a playful personality", 40 | image: "/assets/images/luna.jpg", 41 | price: 350, 42 | available: true, 43 | createdAt: "2023-07-10T14:15:00Z", 44 | updatedAt: "2023-07-10T14:15:00Z", 45 | tags: ["elegant", "vocal", "playful"], 46 | health: { 47 | vaccinated: true, 48 | neutered: true, 49 | medicalHistory: ["Regular checkup - 2023-06-15"], 50 | }, 51 | }, 52 | ]; 53 | 54 | /** 55 | * In-memory reviews database 56 | * Exported for use in route handlers. 57 | */ 58 | export const reviews: ReviewType[] = [ 59 | { 60 | id: "review-1", 61 | petId: "pet-1", 62 | userId: "2", 63 | username: "user", 64 | rating: 5, 65 | comment: "Max is such a wonderful companion! Highly recommend this breed.", 66 | createdAt: "2023-08-20T09:45:00Z", 67 | }, 68 | ]; 69 | -------------------------------------------------------------------------------- /example/definitions.ts: -------------------------------------------------------------------------------- 1 | export const routes = { 2 | GET_live: { 3 | path: "/live", 4 | method: "get", 5 | title: "WebSocket endpoint for real-time pet updates.", 6 | }, 7 | GET_petBy$id_reviews: { 8 | path: "/petBy/:id/reviews", 9 | method: "get", 10 | query: { "sort": "string" }, 11 | title: "Get all reviews for a specific pet", 12 | }, 13 | GET_: { 14 | path: "/", 15 | method: "get", 16 | title: "Returns API information and status", 17 | }, 18 | GET_error: { 19 | path: "/error", 20 | method: "get", 21 | title: 22 | "Route that intentionally throws an error (for testing global error handling)", 23 | }, 24 | GET_export_docs$format: { 25 | path: "/export/docs/:format", 26 | method: "get", 27 | title: 28 | "Export API documentation in different formats (json, yaml, markdown)", 29 | }, 30 | GET_health: { 31 | path: "/health", 32 | method: "get", 33 | title: "API health check endpoint", 34 | }, 35 | GET_serve$0: { 36 | path: "/serve/*", 37 | method: "get", 38 | title: "Serve files from the file system based on wildcard path parameter.", 39 | }, 40 | GET_static$0: { 41 | path: "/static/*", 42 | method: "get", 43 | title: "Serve static files for download or in-browser display.", 44 | }, 45 | GET_stats: { 46 | path: "/stats", 47 | method: "get", 48 | title: "Get shop statistics (admin only)", 49 | }, 50 | GET_petBy$id: { 51 | path: "/petBy/:id", 52 | method: "get", 53 | title: "Retrieve detailed information about a specific pet by ID", 54 | }, 55 | GET_petBy$id_gallery: { 56 | path: "/petBy/:id/gallery", 57 | method: "get", 58 | title: 59 | "Get images for a specific pet (returns main image URL in this sample)", 60 | }, 61 | GET_pets: { 62 | path: "/pets", 63 | method: "get", 64 | title: "Retrieves a list of pets with filtering and pagination options", 65 | }, 66 | GET_pets_search: { 67 | path: "/pets/search", 68 | method: "get", 69 | title: "Advanced search for pets by various criteria", 70 | }, 71 | POST_auth_login: { 72 | path: "/auth/login", 73 | method: "post", 74 | body: { "username": "string", "password": "string" }, 75 | title: "Authenticate a user and receive an access token", 76 | }, 77 | POST_petBy$id_reviews: { 78 | path: "/petBy/:id/reviews", 79 | method: "post", 80 | body: { "rating": 1, "comment": "string" }, 81 | title: "Add a review for a specific pet (authenticated users only)", 82 | }, 83 | POST_upload: { 84 | path: "/upload", 85 | method: "post", 86 | body: { 87 | "image": "file", 88 | "document": "file", 89 | "title": "string", 90 | "description": "string", 91 | "tags": "string", 92 | }, 93 | title: 94 | "Upload files with metadata (admin only) - expects multipart/form-data", 95 | }, 96 | POST_pets: { 97 | path: "/pets", 98 | method: "post", 99 | body: { 100 | "name": "string", 101 | "species": "string", 102 | "breed": "string", 103 | "age": 1, 104 | "gender": "string", 105 | "color": "string", 106 | "description": "string", 107 | "image": "file", 108 | "price": 1, 109 | "available": true, 110 | "tags": [], 111 | "health": {}, 112 | }, 113 | title: "Add a new pet to the inventory (admin only)", 114 | }, 115 | POST_recipes$id_image: { 116 | path: "/recipes/:id/image", 117 | method: "post", 118 | body: { "image": "file" }, 119 | title: "Upload an image for a specific pet (admin only)", 120 | }, 121 | PUT_petBy$id: { 122 | path: "/petBy/:id", 123 | method: "put", 124 | body: { 125 | "name": "string", 126 | "species": "string", 127 | "breed": "string", 128 | "age": 1, 129 | "gender": "string", 130 | "color": "string", 131 | "description": "string", 132 | "image": "file", 133 | "price": 1, 134 | "available": true, 135 | "tags": [], 136 | "health": {}, 137 | }, 138 | title: "Update an existing pet's information (admin only)", 139 | }, 140 | DELETE_reviews$reviewId: { 141 | path: "/reviews/:reviewId", 142 | method: "delete", 143 | title: "Delete a review (admin or review owner only)", 144 | }, 145 | DELETE_petBy$id: { 146 | path: "/petBy/:id", 147 | method: "delete", 148 | title: "Remove a pet from the inventory (admin only)", 149 | }, 150 | } as const; 151 | -------------------------------------------------------------------------------- /example/index.jet.ts: -------------------------------------------------------------------------------- 1 | // src/index.ts 2 | 3 | import { Jetpath } from "../dist/index.js"; 4 | // Create mock plugin files in src/plugins if you don't have real ones. 5 | import { authPlugin } from "./plugins/auth.js"; 6 | import { jetLogger } from "./plugins/logging.js"; 7 | // --- Application Initialization --- 8 | 9 | // Create a new Jetpath application instance with configuration. 10 | // Route handlers and global middleware are automatically detected 11 | // by Jetpath based on their exported names from imported modules. 12 | const app = new Jetpath({ 13 | // Strict mode can enforce certain behaviors (e.g., strict content type checking). 14 | strictMode: "ON", // Example from app.jet.ts 15 | generatedRoutesFilePath: "./example/definitions.ts", 16 | // Configure API documentation (Swagger UI). 17 | // This makes it easy to visualize and test the API endpoints. 18 | apiDoc: { 19 | display: "UI", // Set to "UI" to enable the Swagger UI 20 | name: "PetShop API", // Using PetShop name from original sample 21 | // Use Markdown for a rich description in the documentation. 22 | // This info can be more general, as route-specific info comes from .info() calls. 23 | info: ` 24 | #### PetShop API Documentation 25 | 26 | This is an API for managing a pet shop inventory, built with the **Jetpath** cross-runtime framework. 27 | 28 | It demonstrates various Jetpath features including: 29 | - Convention-over-configuration for routing and middleware. 30 | - Modular project structure. 31 | - Cross-runtime capabilities. 32 | - Authentication and authorization (via plugin). 33 | - Comprehensive logging (via plugin). 34 | - Robust error handling (via global middleware) 35 | 36 | [check our docs for more info](https://jetpath.codedynasty.dev) 37 | `, 38 | 39 | color: "#7e57c2", // Using the color from the original sample 40 | // Optional: Secure the documentation UI itself with basic authentication (example from sample). 41 | username: "admin", // Default username from sample 42 | password: "1234", // Default password from sample **WARNING:** Do not use simple passwords in production. Use environment variables. 43 | }, 44 | source: "./example", 45 | // Configure global headers to be sent with all responses. 46 | globalHeaders: { 47 | "X-Pet-API-Version": "1.0.0", // Example custom header from sample 48 | "Access-Control-Allow-Origin": "*", // **WARNING:** In production, replace "*" with the specific origin(s) of your frontend application(s) for security. 49 | "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", 50 | "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Request-ID", // Include Authorization and X-Request-ID headers 51 | "Content-Type": "application/json", // Default response content type 52 | }, 53 | 54 | // Configure the port for the server to listen on. 55 | port: process.env.PORT ? parseInt(process.env.PORT, 10) : 9000, // Using sample's port 56 | 57 | // Enable WebSocket upgrades. This is required for the /live WebSocket endpoint to work. 58 | upgrade: true, 59 | // Optional: Configure a directory for serving static files automatically (alternative to GET_static$0 route) 60 | // static: "./public", // Example 61 | }); 62 | 63 | // --- Add Plugins --- 64 | // Add plugin instances to the application. Plugins provide extended functionality. 65 | // Access plugins via ctx.plugins in middleware and route handlers. 66 | 67 | // Add the logger plugin 68 | // Configure the logger plugin first if needed, then add it. 69 | jetLogger.config = { 70 | level: process.env.NODE_ENV === "production" ? "info" : "debug", 71 | format: "json", // Log format (json or text) 72 | filename: "./pet-shop-api-log.log", // Log file path relative to project root 73 | }; 74 | 75 | app.derivePlugins(jetLogger, authPlugin); 76 | 77 | app.listen(); 78 | -------------------------------------------------------------------------------- /example/middleware/global.ts: -------------------------------------------------------------------------------- 1 | // src/middleware/global.ts 2 | 3 | import { JetMiddleware } from "../../dist"; 4 | // Assuming AuthPluginType and jetLoggerType are defined or imported from plugin types 5 | import { type AuthPluginType } from "../plugins/auth"; // Assuming auth plugin types are here 6 | import { type jetLoggerType } from "../plugins/logging"; // Assuming logger plugin types are here 7 | 8 | /** 9 | * Global middleware for request processing and error handling 10 | * This middleware runs for all routes and handles: 11 | * - Request logging 12 | * - Authentication verification (when needed) 13 | * - Error processing 14 | * - Response formatting 15 | * 16 | * It is automatically applied by Jetpath because of its exported name `MIDDLEWARE_`. 17 | * 18 | * @param {Object} ctx - The request context (pre-handler) 19 | * @returns {Function} The post-handler middleware function 20 | */ 21 | export const MIDDLEWARE_: JetMiddleware<{}, [AuthPluginType, jetLoggerType]> = ( 22 | ctx, 23 | ) => { 24 | // --- Pre-handler Logic (runs before the route handler) --- 25 | const startTime = Date.now(); 26 | // Generate a unique request ID (example from app.jet.ts) 27 | const requestId = `req-${Date.now()}-${Math.floor(Math.random() * 1000)}`; 28 | ctx.set("X-Request-ID", requestId); // Add request ID to response headers 29 | 30 | // Log initial request info using the logger plugin (if available) 31 | // Use optional chaining in case plugins aren't correctly loaded/typed 32 | ctx.plugins?.info( 33 | ctx, 34 | "Request received", 35 | ); 36 | 37 | // Authentication verification (example from app.jet.ts) 38 | // Skip auth check for public routes 39 | const isPublicRoute = ctx.request.url.includes("/auth/login") || 40 | ctx.request.url.includes("/api-doc") || // api-doc is public 41 | ctx.request.url.includes("/export/docs") || // documentation export is public 42 | ctx.request.url.includes("/health") || // health check is public 43 | ctx.request.url === "/" || // root is public 44 | ctx.request.url.startsWith("/static/") || // static files are public (based on GET_static$0) 45 | ctx.request.url.startsWith("/serve/"); // served files are public (based on GET_serve$0) 46 | // Note: Pet list and detail (GET /pets, GET /petBy/:id) were public in the sample, 47 | // while POST/PUT/DELETE pets and others were protected. 48 | // We'll leave the authentication check logic as it was in the sample for now. 49 | 50 | // Verify authentication for protected routes 51 | // The original sample checked auth for any non-public route unless it was the root GET. 52 | // Let's refine this check slightly for clarity, assuming auth is needed *unless* it's explicitly public. 53 | const requiresAuth = !isPublicRoute && 54 | !ctx.request.url.startsWith("/pets") && // Assuming GET /pets and /petBy/:id are public 55 | !ctx.request.url.startsWith("/petBy/") && // GET pet details are public 56 | !ctx.request.url.startsWith("/reviews"); // Assuming GET reviews are public 57 | 58 | if (requiresAuth) { // Only run auth check if the route is not public 59 | const auth = ctx.plugins?.auth?.verifyAuth(ctx); // Use optional chaining for safety 60 | if (!auth?.authenticated) { 61 | ctx.code = 401; // Unauthorized 62 | ctx.set("WWW-Authenticate", "Bearer"); // Suggest Bearer auth 63 | ctx.plugins?.warn( 64 | ctx, 65 | "Authentication failed", 66 | ); 67 | // We don't ctx.throw here; the post-handler will process the 401 code. 68 | } else { 69 | // Attach user info to context state for use in route handlers 70 | ctx.state["user"] = auth.user; 71 | } 72 | } 73 | 74 | // --- Post-handler Logic (runs after the route handler, or if an error occurs) --- 75 | // This function is returned by the pre-handler and receives the context and potential error. 76 | return (ctx, err: any) => { 77 | const duration = Date.now() - startTime; // Calculate total request duration. 78 | 79 | // Add standard response header 80 | ctx.set("X-Response-Time", `${duration}ms`); 81 | 82 | // --- Error Handling --- 83 | if (err) { 84 | // An error occurred. Log it using the logger plugin. 85 | ctx.plugins?.error( 86 | ctx, 87 | "Request failed due to error", 88 | ); 89 | 90 | // Determine the status code for the error response. 91 | ctx.code = ctx.code >= 400 92 | ? ctx.code 93 | : (err.statusCode && err.statusCode >= 400 ? err.statusCode : 500); 94 | 95 | // Send a standardized JSON error response. 96 | ctx.send({ 97 | status: "error", 98 | message: ctx.code === 500 && process.env.NODE_ENV === "production" 99 | ? "An internal server error occurred." 100 | : err.message || "Internal server error", // Use error message in dev or for client errors 101 | requestId, 102 | timestamp: new Date().toISOString(), 103 | // Include stack trace in non-production environments 104 | ...(process.env.NODE_ENV !== "production" && err instanceof Error && 105 | err.stack && { stack: err.stack.split("\n") }), 106 | }); 107 | 108 | // Return to stop further processing of this error by Jetpath. 109 | return; 110 | } 111 | 112 | // --- 404 Handling --- 113 | // If no route matched, Jetpath sets ctx.code to 404. Handle this specifically. 114 | if (ctx.code === 404) { 115 | ctx.plugins?.warn( 116 | ctx, 117 | "Resource not found (404)", 118 | ); 119 | 120 | ctx.send({ 121 | status: "error", 122 | message: "The requested resource was not found", 123 | requestId, 124 | timestamp: new Date().toISOString(), 125 | }); 126 | return; // Return to stop further processing 127 | } 128 | 129 | // --- Successful Response Logging --- 130 | // If no error and code is not 404, it's a successful response. 131 | ctx.plugins?.info( 132 | ctx, 133 | "Request completed successfully", 134 | ); 135 | 136 | // If the post-handler doesn't return anything (or returns undefined), 137 | // Jetpath continues with the response process. 138 | }; 139 | }; 140 | -------------------------------------------------------------------------------- /example/plugins/auth.ts: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // PLUGINS CONFIGURATION 3 | // ============================================================================= 4 | 5 | import { type JetContext } from "../../dist/index.js"; 6 | 7 | /** 8 | * Auth Plugin - Provides authentication and authorization functionality 9 | * 10 | * This plugin adds methods for token generation, validation, and user 11 | * authentication that can be used across routes. 12 | */ 13 | export const authPlugin = { 14 | name: "authPlugin", 15 | executor() { 16 | // In a real application, use a secure secret management solution 17 | const ADMIN_API_KEY = process.env["ADMIN_API_KEY"] || "admin-secret-key"; 18 | 19 | // Simple in-memory user store (use a database in production) 20 | const users = [ 21 | { id: "1", username: "admin", password: "admin123", role: "admin" }, 22 | { id: "2", username: "user", password: "user123", role: "customer" }, 23 | ]; 24 | 25 | return { 26 | /** 27 | * Validates user credentials and returns a token 28 | */ 29 | authenticateUser(username: string, password: string) { 30 | const user = users.find((u) => 31 | u.username === username && u.password === password 32 | ); 33 | if (!user) { 34 | return { authenticated: false, message: "Invalid credentials" }; 35 | } 36 | 37 | // In production, use proper JWT library 38 | const token = `token-${user.id}-${Date.now()}`; 39 | return { 40 | authenticated: true, 41 | token, 42 | user: { id: user.id, username: user.username, role: user.role }, 43 | }; 44 | }, 45 | 46 | /** 47 | * Verifies if a request has valid authentication 48 | */ 49 | verifyAuth(ctx: JetContext) { 50 | const authHeader = ctx.get("authorization"); 51 | 52 | if (!authHeader || !authHeader.startsWith("Bearer ")) { 53 | return { authenticated: false, message: "Missing or invalid token" }; 54 | } 55 | 56 | const token = authHeader.split(" ")[1]; 57 | // In production, implement proper token validation 58 | const userId = token.split("-")[1]; 59 | const user = users.find((u) => u.id === userId); 60 | 61 | if (!user) { 62 | return { authenticated: false, message: "Invalid token" }; 63 | } 64 | 65 | return { 66 | authenticated: true, 67 | user: { id: user.id, username: user.username, role: user.role }, 68 | }; 69 | }, 70 | 71 | /** 72 | * Verifies if the request is from an admin 73 | */ 74 | isAdmin(ctx: JetContext): boolean { 75 | // Check for admin API key 76 | if (ctx.get("x-admin-Key") === ADMIN_API_KEY) { 77 | return true; 78 | } 79 | 80 | // Alternatively check user role 81 | const auth = this["verifyAuth"](ctx); 82 | // @ts-ignore 83 | return auth.authenticated && auth.user.role === "admin"; 84 | }, 85 | }; 86 | }, 87 | }; 88 | 89 | export type AuthPluginType = ReturnType; 90 | -------------------------------------------------------------------------------- /example/plugins/logging.ts: -------------------------------------------------------------------------------- 1 | import { appendFileSync } from "node:fs"; 2 | import { resolve } from "node:path"; 3 | import { config } from "node:process"; 4 | 5 | /** 6 | * Supported log levels 7 | */ 8 | export type LogLevel = "trace" | "debug" | "info" | "warn" | "error"; 9 | const levelRank: Record = { 10 | trace: 10, 11 | debug: 20, 12 | info: 30, 13 | warn: 40, 14 | error: 50, 15 | }; 16 | 17 | /** 18 | * Configuration options for the logging plugin 19 | */ 20 | interface LoggingConfig { 21 | level?: LogLevel; // minimum level to emit 22 | format?: "json" | "text"; // format of log entries 23 | filename?: string; // file to write logs to 24 | getRequestId?: (req: Request) => string; // extract or generate a request ID 25 | transports?: Transport[]; // custom transports (overrides filename/format defaults) 26 | } 27 | 28 | /** 29 | * Structure of a log entry 30 | */ 31 | interface LogEntry { 32 | timestamp: string; 33 | level: LogLevel; 34 | message?: string; 35 | method: string; 36 | url: string; 37 | requestId?: string; 38 | meta?: Record; 39 | } 40 | 41 | /** Transport interface for log targets */ 42 | interface Transport { 43 | log(entry: LogEntry): Promise | void; 44 | } 45 | 46 | /** Default console transport, respects JSON or text */ 47 | class ConsoleTransport implements Transport { 48 | private format: "json" | "text" = "json"; 49 | constructor(format: "json" | "text" = "json") { 50 | this.format = format; 51 | } 52 | log(entry: LogEntry) { 53 | const line = this.format === "json" 54 | ? JSON.stringify(entry) 55 | : `${entry.timestamp} [${entry.level.toUpperCase()}] ${entry.method} ${entry.url} - ${entry.message}${ 56 | Object.keys(entry.meta || {}).length 57 | ? " " + JSON.stringify(entry.meta) 58 | : "" 59 | }`; 60 | console.log(line); 61 | } 62 | } 63 | 64 | /** File transport for Node/Bun/Deno, appends to given filename */ 65 | class FileTransport implements Transport { 66 | private filename: string; 67 | private format: "json" | "text" = "json"; 68 | constructor( 69 | filename: string, 70 | format: "json" | "text" = "json", 71 | ) { 72 | this.filename = filename; 73 | this.format = format; 74 | } 75 | log(entry: LogEntry) { 76 | const line = this.format === "json" 77 | ? JSON.stringify(entry) 78 | : `${entry.timestamp} [${entry.level.toUpperCase()}] ${entry.method} ${entry.url} - ${entry.message}${ 79 | Object.keys(entry.meta || {}).length 80 | ? " " + JSON.stringify(entry.meta) 81 | : "" 82 | }`; 83 | // Cross-runtime append 84 | appendFileSync(resolve(this.filename), line + "\n"); 85 | } 86 | } 87 | 88 | /** 89 | * Creates a JetPlugin with structured logging capabilities 90 | */ 91 | export const jetLogger = { 92 | name: "jetLogger", 93 | /** 94 | * Creates a logging plugin instance 95 | * @param config Configuration options for the logger 96 | */ 97 | config: { 98 | level: "info", 99 | format: "json", 100 | filename: undefined, 101 | transports: undefined, 102 | } as LoggingConfig, 103 | executor() { 104 | const { 105 | level = "info", 106 | format = "json", 107 | filename, 108 | getRequestId, 109 | transports: customTransports, 110 | } = this.config; 111 | // Determine transports: custom, or build from filename/console defaults 112 | const transports: Transport[] = customTransports 113 | ? customTransports 114 | : filename 115 | ? [new FileTransport(filename, format)] 116 | : [new ConsoleTransport(format)]; 117 | // Send entry to all configured transports 118 | async function emit(entry: LogEntry) { 119 | for (const t of transports) { 120 | try { 121 | await t.log(entry); 122 | } catch (err) { 123 | console.error("Logging transport error:", err); 124 | } 125 | } 126 | } 127 | 128 | // Level filtering 129 | function shouldLog(entryLevel: LogLevel) { 130 | return levelRank[entryLevel] >= levelRank[level]; 131 | } 132 | 133 | // Build the entry object 134 | function buildEntry( 135 | entryLevel: LogLevel, 136 | ctx: { request: Request }, 137 | message?: string, 138 | meta?: Record, 139 | ): LogEntry { 140 | const { request } = ctx; 141 | return { 142 | timestamp: new Date().toISOString(), 143 | level: entryLevel, 144 | message: typeof message === "string" 145 | ? message 146 | : JSON.stringify(message), 147 | method: request.method, 148 | url: request.url, 149 | requestId: getRequestId?.(request), 150 | meta, 151 | }; 152 | } 153 | 154 | // Exposed log methods 155 | return { 156 | trace( 157 | ctx: { request: Request }, 158 | message: any, 159 | meta?: Record, 160 | ) { 161 | if (!shouldLog("trace")) return; 162 | emit(buildEntry("trace", ctx, message, meta)); 163 | }, 164 | debug( 165 | ctx: { request: Request }, 166 | message: any, 167 | meta?: Record, 168 | ) { 169 | if (!shouldLog("debug")) return; 170 | emit(buildEntry("debug", ctx, message, meta)); 171 | }, 172 | info( 173 | ctx: { request: Request }, 174 | message?: any, 175 | meta?: Record, 176 | ) { 177 | if (!shouldLog("info")) return; 178 | emit(buildEntry("info", ctx, message, meta)); 179 | }, 180 | warn( 181 | ctx: { request: Request }, 182 | message: any, 183 | meta?: Record, 184 | ) { 185 | if (!shouldLog("warn")) return; 186 | emit(buildEntry("warn", ctx, message, meta)); 187 | }, 188 | error( 189 | ctx: { request: Request }, 190 | message: any, 191 | meta?: Record, 192 | ) { 193 | if (!shouldLog("error")) return; 194 | emit(buildEntry("error", ctx, message, meta)); 195 | }, 196 | }; 197 | }, 198 | }; 199 | 200 | export type jetLoggerType = ReturnType; 201 | -------------------------------------------------------------------------------- /example/routes/auth.jet.ts: -------------------------------------------------------------------------------- 1 | // src/routes/auth.ts 2 | 3 | import { type JetRoute, use } from "../../dist/index.js"; 4 | import { type AuthPluginType } from "../plugins/auth.js"; // Import AuthPluginType 5 | 6 | // --- Authentication Route --- 7 | 8 | /** 9 | * Authentication endpoint - Login with username and password 10 | * @route POST /auth/login 11 | * @access Public 12 | * Demonstrates: POST request, body parsing, plugin usage (auth), sending token. 13 | */ 14 | export const POST_auth_login: JetRoute< 15 | { body: { username: string; password: string } }, 16 | [AuthPluginType] 17 | > = async function (ctx) { 18 | // Parse the request body. Jetpath handles this. 19 | await ctx.parse(); 20 | const { username, password } = ctx.body; 21 | 22 | // Use the auth plugin to authenticate the user. 23 | // Access plugins via ctx.plugins, assuming the plugin was added with app.addPlugin(authPlugin) in index.ts. 24 | const authResult = ctx.plugins.authenticateUser(username, password); 25 | 26 | // Check authentication result. 27 | if (!authResult.authenticated) { 28 | ctx.code = 401; // Unauthorized status code. 29 | ctx.send({ status: "error", message: authResult.message }); // Send error response. 30 | return; // Stop further processing. 31 | } 32 | 33 | // If authentication is successful, send success response with token and user info. 34 | ctx.send({ 35 | status: "success", 36 | message: "Authentication successful", 37 | token: authResult.token, // Send the generated token. 38 | user: { // Send basic user info. 39 | id: authResult.user?.id, 40 | username: authResult.user?.username, 41 | role: authResult.user?.role, 42 | }, 43 | }); 44 | }; 45 | 46 | // Apply body validation and .info() for documentation using use() chained after the function definition. 47 | use(POST_auth_login).body((t) => { 48 | // Define the expected request body structure and validation rules. 49 | return { 50 | username: t.string({ 51 | err: "Username is required", 52 | inputDefaultValue: "admin", 53 | }).required(), // Username must be a required string. 54 | password: t.string({ 55 | err: "Password is required", 56 | inputDefaultValue: "admin123", 57 | }).required(), // Password must be a required string. 58 | }; 59 | }).title("Authenticate a user and receive an access token") // Add info for documentation. 60 | .description(` 61 | ### ok here 62 | `); 63 | -------------------------------------------------------------------------------- /example/routes/live.jet.ts: -------------------------------------------------------------------------------- 1 | // src/routes/live.ts 2 | 3 | import { type JetRoute, use } from "../../dist/index.js"; 4 | // Import types if needed, e.g., for initial stats message 5 | import { pets } from "../data/models.js"; // Import pets data for initial stats message 6 | 7 | // --- WebSocket Server --- 8 | // This route handles WebSocket connections for real-time updates. 9 | 10 | // Keep track of connected WebSocket clients using a Set. 11 | const connectedSockets = new Set(); 12 | 13 | // --- WebSocket Broadcasting --- 14 | // Function to broadcast messages to all connected clients. 15 | const broadcastMessage = (message: string) => { 16 | // Iterate over all connected sockets. 17 | const messageJson = JSON.stringify({ 18 | type: "update", 19 | message, 20 | timestamp: new Date().toISOString(), 21 | }); // Format message as JSON 22 | 23 | connectedSockets.forEach((socket) => { 24 | // Check if the socket connection is open before sending. 25 | if (socket.readyState === WebSocket.OPEN) { 26 | socket.send(messageJson); // Send the message to the client. 27 | } 28 | }); 29 | console.log( 30 | `Broadcasted message to ${connectedSockets.size} clients: "${message}"`, 31 | ); 32 | }; 33 | 34 | // Export the broadcast function so other modules (like recipes) can use it. 35 | export { broadcastMessage }; 36 | 37 | /** 38 | * WebSocket Endpoint for real-time updates. 39 | * @route GET /live 40 | * @access Public 41 | * Demonstrates: Handling WebSocket connections, sending/receiving messages, broadcasting. 42 | */ 43 | export const GET_live: JetRoute = (ctx) => { 44 | // Initiate the WebSocket upgrade handshake. 45 | // This tells Jetpath to switch from HTTP to WebSocket protocol for this connection. 46 | ctx.upgrade(); 47 | 48 | // After a successful upgrade, ctx.connection or a similar property holds the WebSocket object. 49 | // The exact way to access the WebSocket object might depend on the Jetpath version/adapter. 50 | // The original sample uses `const conn = ctx.connection!`. Let's stick to that assumption. 51 | const ws = ctx.connection; // Assuming ctx.connection is the WebSocket or similar 52 | 53 | // If the upgrade fails, handle the error (though the middleware might catch it). 54 | if (!ws) { 55 | ctx.code = 500; 56 | ctx.send({ status: "error", message: "Failed to upgrade to WebSocket." }); 57 | console.error("WebSocket upgrade failed for request:", ctx.request.url); 58 | return; // Stop processing if no WebSocket connection was established. 59 | } 60 | 61 | // --- WebSocket Event Listeners --- 62 | // Attach event listeners to the WebSocket object to handle connection events and messages. 63 | 64 | // When the WebSocket connection is opened successfully. 65 | ws.addEventListener("open", (socket) => { 66 | console.log("WebSocket client connected."); 67 | connectedSockets.add(socket); // Add the new socket to our set of connected clients. 68 | 69 | // Send a welcome message to the new client. 70 | // You could include initial data like current stats. 71 | const availablePets = pets.filter((pet) => pet.available).length; 72 | socket.send(JSON.stringify({ 73 | type: "info", 74 | message: "Connected to PetShop live updates.", 75 | currentStats: { 76 | totalPets: pets.length, 77 | availablePets: availablePets, 78 | }, 79 | timestamp: new Date().toISOString(), 80 | })); 81 | 82 | // Inform other clients about the new connection (optional). 83 | broadcastMessage( 84 | `A new client connected (Total active connections: ${connectedSockets.size}).`, 85 | ); 86 | }); 87 | 88 | // When a message is received from a client. 89 | ws.addEventListener("message", (socket, event) => { 90 | const message = event.data; // The data received from the client. 91 | console.log("WebSocket message received:", message); 92 | // Handle incoming messages from clients if needed. 93 | // For this simple sample, we'll respond to a 'ping' message. 94 | try { 95 | const parsedMessage = typeof message === "string" 96 | ? JSON.parse(message) 97 | : message; 98 | if (parsedMessage.type === "ping") { 99 | // Respond with a pong message for health checks. 100 | socket.send( 101 | JSON.stringify({ type: "pong", timestamp: new Date().toISOString() }), 102 | ); 103 | } else { 104 | // If the message is not recognized, you could broadcast it or handle it differently. 105 | // For this sample, we'll just log and ignore other messages. 106 | broadcastMessage(message); 107 | } 108 | // You could add more complex command handling here based on message.type or content. 109 | } catch (e) { 110 | console.error("Failed to parse or handle WebSocket message:", e); 111 | socket.send( 112 | JSON.stringify({ 113 | type: "error", 114 | message: "Invalid message format (expected JSON)", 115 | }), 116 | ); 117 | } 118 | }); 119 | 120 | // When the WebSocket connection is closed by either the client or server. 121 | ws.addEventListener("close", (socket, event) => { 122 | console.log( 123 | `WebSocket client disconnected (Code: ${event.code}, Reason: ${ 124 | event.reason || "No reason" 125 | }).`, 126 | ); 127 | connectedSockets.delete(socket); // Remove the closed socket from the set. 128 | // Inform other clients about the disconnection (optional). 129 | broadcastMessage( 130 | `A client disconnected (Total active connections: ${connectedSockets.size}).`, 131 | ); 132 | }); 133 | 134 | // The initial HTTP request handler does not send a response after calling ctx.upgrade(). 135 | // The connection is now managed by the WebSocket event listeners and the server's WebSocket handling. 136 | }; 137 | 138 | // Apply .info() for documentation. 139 | // This describes the GET request that initiates the WebSocket handshake. 140 | use(GET_live).title("WebSocket endpoint for real-time pet updates."); // Adjusted info message 141 | 142 | // Export the route handler. 143 | -------------------------------------------------------------------------------- /example/routes/reviews.jet.ts: -------------------------------------------------------------------------------- 1 | // src/routes/reviews.ts 2 | 3 | import { type JetRoute, use } from "../../dist/index.js"; 4 | // Import AuthPluginType if authentication checks are done within route handlers 5 | import { type AuthPluginType } from "../plugins/auth.js"; 6 | // Import data models and in-memory data arrays 7 | import { pets, reviews } from "../data/models.js"; 8 | import { type ReviewType } from "../types.js"; // Import ReviewType 9 | 10 | // --- Reviews Management Routes --- 11 | 12 | /** 13 | * Get all reviews for a pet 14 | * @route GET /petBy/:id/reviews 15 | * @access Public 16 | * Demonstrates: Dynamic GET route ($id), path parameter, filtering related data, sorting, calculating aggregates (average rating). 17 | */ 18 | export const GET_petBy$id_reviews: JetRoute<{ 19 | params: { id: string }; // Pet ID from path 20 | query: { sort?: string }; // Optional sort query parameter 21 | }> = function (ctx) { 22 | const petId = ctx.params.id; // Access pet ID from path. 23 | const sort = ctx.query.sort || "-createdAt"; // Access sort query param, default to newest first. 24 | 25 | // Find the pet to ensure it exists. 26 | const pet = pets.find((p) => p.id === petId); 27 | 28 | if (!pet) { 29 | ctx.code = 404; // Not Found 30 | ctx.send({ 31 | status: "error", 32 | message: `Pet with ID ${petId} not found.`, 33 | }); 34 | return; 35 | } 36 | 37 | // Filter reviews to get only those for the specified pet. 38 | let petReviews = reviews.filter((review) => review.petId === petId); 39 | 40 | // Sort the filtered reviews based on the sort query parameter. 41 | const sortField = sort.startsWith("-") ? sort.substring(1) : sort; // Get field name (remove leading '-') 42 | const sortDirection = sort.startsWith("-") ? -1 : 1; // Determine sort direction (1 for asc, -1 for desc) 43 | 44 | // Sort the array. Using `any` for simplicity due to dynamic sortField access. 45 | petReviews.sort((a: any, b: any) => { 46 | const valueA = a[sortField]; 47 | const valueB = b[sortField]; 48 | 49 | if (valueA < valueB) return -1 * sortDirection; 50 | if (valueA > valueB) return 1 * sortDirection; 51 | return 0; // Values are equal 52 | }); 53 | 54 | // Calculate aggregate statistics for the reviews (e.g., average rating). 55 | const totalRating = petReviews.reduce( 56 | (sum, review) => sum + review.rating, 57 | 0, 58 | ); // Sum of all ratings 59 | const averageRating = petReviews.length > 0 60 | ? totalRating / petReviews.length 61 | : 0; // Calculate average, handle division by zero. 62 | 63 | // Send the response with the filtered, sorted reviews and statistics. 64 | ctx.send({ 65 | status: "success", 66 | petId: petId, 67 | petName: pet.name, 68 | stats: { // Include review statistics 69 | count: petReviews.length, 70 | averageRating: averageRating, 71 | }, 72 | reviews: petReviews, // Include the list of reviews 73 | }); 74 | }; 75 | 76 | // Apply .info() for documentation. 77 | use(GET_petBy$id_reviews).title("Get all reviews for a specific pet").query( 78 | (t) => { 79 | // Define the expected query parameters and validation rules. 80 | return { 81 | // Optional sort parameter, default to '-createdAt' (newest first). 82 | sort: t.string({ 83 | err: "Sort parameter must be a string", 84 | }).default("-createdAt"), 85 | }; 86 | }, 87 | ); 88 | 89 | /** 90 | * Add a review for a pet 91 | * @route POST /petBy/:id/reviews 92 | * @access Authenticated (Based on sample's middleware check) 93 | * Demonstrates: POST request, dynamic routing ($id), body parsing, input validation (use().body()), data insertion. 94 | */ 95 | export const POST_petBy$id_reviews: JetRoute<{ 96 | params: { id: string }; // Pet ID from path 97 | body: { // Expected request body structure 98 | rating: number; 99 | comment: string; 100 | }; 101 | }, [AuthPluginType]> = async function (ctx) { 102 | // Check if user is authenticated (access user and authenticated status from ctx.state/plugins) 103 | // The global middleware sets ctx.state.user if authenticated. 104 | const user = ctx.state["user"]; 105 | if (!user) { 106 | ctx.code = 401; // Unauthorized 107 | ctx.send({ 108 | status: "error", 109 | message: "Authentication required to post reviews", 110 | }); 111 | return; 112 | } 113 | 114 | const petId = ctx.params.id; // Access pet ID from path. 115 | 116 | // Find the pet to ensure it exists before adding a review. 117 | const pet = pets.find((p) => p.id === petId); 118 | 119 | if (!pet) { 120 | ctx.code = 404; // Not Found 121 | ctx.send({ 122 | status: "error", 123 | message: `Pet with ID ${petId} not found.`, 124 | }); 125 | return; 126 | } 127 | 128 | // Parse and validate the request body. Jetpath handles this via use().body(). 129 | await ctx.parse(); // Ensure body is parsed 130 | const { rating, comment } = ctx.body; // Access the validated body 131 | 132 | // Create a new review object with a unique ID and current timestamp. 133 | const newReview: ReviewType = { 134 | id: `review-${Date.now()}-${Math.random().toString(36).substring(2, 15)}`, // Generate unique ID 135 | petId: petId, // Link to the pet 136 | userId: user.id, // Link to the authenticated user's ID 137 | username: user.username, // Store the reviewer's username 138 | rating: rating, // Rating from the request body 139 | comment: comment, // Comment from the request body 140 | createdAt: new Date().toISOString(), // Set creation timestamp 141 | }; 142 | 143 | // Add the new review to the in-memory database array. 144 | reviews.push(newReview); 145 | 146 | // Log the review creation action. 147 | // Log the review creation action. 148 | ctx.plugins?.["info"]({ 149 | action: "create_review", 150 | reviewId: newReview.id, 151 | petId: newReview.petId, 152 | userId: newReview.userId, 153 | message: `User ${newReview.username} added review for pet ${pet.name}`, 154 | }); 155 | 156 | // Send a 201 Created response with the newly created review details. 157 | ctx.code = 201; // Created status code. 158 | ctx.send({ 159 | status: "success", 160 | message: "Review added successfully", 161 | review: newReview, 162 | }); 163 | }; 164 | 165 | // Apply body validation and .info() for documentation using use() chained after the function definition. 166 | use(POST_petBy$id_reviews).body((t) => { 167 | // Define the expected request body structure and validation rules. 168 | return { 169 | // Validate rating as a required number. 170 | rating: t.number({ 171 | err: "Rating is required (1-5)", 172 | }).required(), 173 | // Validate comment as a required string. 174 | comment: t.string({ err: "Review comment is required" }).required(), 175 | }; 176 | }).title("Add a review for a specific pet (authenticated users only)"); 177 | 178 | /** 179 | * Delete a review 180 | * @route DELETE /reviews/:reviewId 181 | * @access Authenticated (Review owner or Admin - Based on sample logic) 182 | * Demonstrates: DELETE request, dynamic routing ($reviewId), path parameter, data deletion, authorization check (owner or admin). 183 | */ 184 | export const DELETE_reviews$reviewId: JetRoute<{ 185 | params: { reviewId: string }; // Review ID from path 186 | }, [AuthPluginType]> = function (ctx) { 187 | // Check if user is authenticated. 188 | const user = ctx.state["user"]; 189 | if (!user) { 190 | ctx.code = 401; // Unauthorized 191 | ctx.send({ 192 | status: "error", 193 | message: "Authentication required to delete reviews", 194 | }); 195 | return; 196 | } 197 | 198 | const reviewId = ctx.params.reviewId; // Access review ID from path. 199 | 200 | // Find index of the review by ID. 201 | const reviewIndex = reviews.findIndex((r) => r.id === reviewId); 202 | 203 | // If review is not found, set 404 status and send error response. 204 | if (reviewIndex === -1) { 205 | ctx.code = 404; // Not Found 206 | ctx.send({ 207 | status: "error", 208 | message: `Review with ID ${reviewId} not found.`, 209 | }); 210 | return; 211 | } 212 | 213 | const review = reviews[reviewIndex]; // Get the review object. 214 | 215 | // Authorization Check: Check if the authenticated user is the owner of the review OR an admin. 216 | const isOwner = review.userId === user.id; 217 | const isAdmin = user.role === "admin"; 218 | 219 | if (!isOwner && !isAdmin) { 220 | ctx.code = 403; // Forbidden 221 | ctx.send({ 222 | status: "error", 223 | message: "You don't have permission to delete this review", 224 | }); 225 | return; 226 | } 227 | 228 | // Remove the review from the in-memory array using splice(). 229 | const deletedReview = reviews.splice(reviewIndex, 1)[0]; 230 | 231 | // Log the deletion action. 232 | ctx.plugins?.["info"]({ 233 | action: "delete_review", 234 | reviewId: deletedReview.id, 235 | petId: deletedReview.petId, 236 | userId: user.id, 237 | message: `User ${user.username} deleted review ${deletedReview.id}`, 238 | }); 239 | 240 | // Send a success response with details of the deleted review. 241 | ctx.send({ 242 | status: "success", 243 | message: `Review with ID ${reviewId} deleted successfully`, 244 | review: deletedReview, 245 | }); 246 | }; 247 | 248 | // Apply .info() for documentation. 249 | use(DELETE_reviews$reviewId).title( 250 | "Delete a review (admin or review owner only)", 251 | ); 252 | 253 | // Export route handlers so Jetpath can discover and register them based on naming convention. 254 | -------------------------------------------------------------------------------- /example/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Pet data model (Based on app.jet.ts typedef comment) 3 | */ 4 | export type PetType = { 5 | id?: string; 6 | name: string; 7 | species: string; 8 | breed: string; 9 | age: number; 10 | gender: string; 11 | color: string; 12 | description: string; 13 | image?: string; 14 | price: number; 15 | available: boolean; 16 | createdAt?: string; 17 | updatedAt?: string; 18 | tags?: string[]; 19 | health?: { 20 | vaccinated?: boolean; 21 | neutered?: boolean; 22 | medicalHistory?: string[]; 23 | }; 24 | }; 25 | 26 | /** 27 | * User review data model (Based on app.jet.ts typedef comment) 28 | */ 29 | export type ReviewType = { 30 | id: string; 31 | petId: string; 32 | userId: string; 33 | username: string; 34 | rating: number; 35 | comment: string; 36 | createdAt: string; 37 | }; 38 | -------------------------------------------------------------------------------- /example/websockets-usage.md: -------------------------------------------------------------------------------- 1 | # Using websockets in jetpath 2 | 3 | ## Deno & Bunjs 4 | 5 | ```js 6 | // usage go to ws://localhost:8000/sockets 7 | 8 | // for deno and bun only 9 | export const GET_sockets: JetRoute = (ctx) => { 10 | ctx.upgrade(); 11 | const conn = ctx.connection!; 12 | try { 13 | conn.addEventListener("open", (socket) => { 14 | console.log("a client connected!"); 15 | socket.send("😎 Welcome to jet chat"); 16 | }); 17 | conn.addEventListener("message", (socket,event) => { 18 | if (event.data === "ping") { 19 | socket.send("pong"); 20 | } else { 21 | socket.send("all your " + event.data + " are belong to us!"); 22 | } 23 | }); 24 | } catch (error) { 25 | console.log(error); 26 | } 27 | }; 28 | 29 | ``` 30 | 31 | ## Node 32 | 33 | ```js 34 | // install 35 | 36 | // npm i ws 37 | 38 | // usage 39 | import { WebSocketServer } from "ws"; 40 | import http from "node:http"; 41 | import { Jetpath } from "jetpath"; 42 | const app = new Jetpath({ source: "tests" }); 43 | 44 | // Spinning the HTTP server and the WebSocket server. 45 | const server = app.server; 46 | const wsServer = new WebSocketServer({ server }); 47 | const port = 8000; 48 | server.listen(port, () => { 49 | console.log(`WebSocket server is running on port ${port}`); 50 | }); 51 | 52 | //? listen for server upgrade via ctx.request 53 | ``` -------------------------------------------------------------------------------- /icon-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeDynasty-dev/Jetpath/ec22ef8a7e056071590def7c0be943ed464398e7/icon-transparent.png -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeDynasty-dev/Jetpath/ec22ef8a7e056071590def7c0be943ed464398e7/icon.png -------------------------------------------------------------------------------- /pack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | #!/bin/bash 3 | 4 | echo " Packing ...." 5 | tsc 6 | bun bundle 7 | npm pack --ignore-scripts # how to avoid running npm prepare here? 8 | mv **.tgz ~/zips && echo "Packed successfully" -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jetpath", 3 | "version": "1.8.6", 4 | "description": "Jetpath - A fast, seamless and minimalist framework for Node, Deno and Bun.js. Embrace the speed and elegance of the next-gen server-side experience.", 5 | "main": "dist/index.js", 6 | "type": "module", 7 | "files": [ 8 | "dist/index.d.ts", 9 | "dist/primitives", 10 | "dist/extracts", 11 | "dist/index.js" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/codedynasty-dev/jetpath.git" 16 | }, 17 | "bin": { 18 | "jetpath": "dist/cli.js" 19 | }, 20 | "keywords": [ 21 | "web", 22 | "framework", 23 | "fast", 24 | "simple", 25 | "bunjs", 26 | "nodejs", 27 | "denojs", 28 | "expressive", 29 | "server", 30 | "http", 31 | "convention" 32 | ], 33 | "author": "friday candour fridaycandours@gmail.com", 34 | "license": "Apache", 35 | "bugs": { 36 | "url": "https://github.com/codedynasty-dev/jetpath/issues" 37 | }, 38 | "homepage": "https://jetpath.codedynasty.dev", 39 | "scripts": { 40 | "compile": "./pack", 41 | "deno": "deno run --allow-all tests/app.jet.ts", 42 | "bun": "bun --watch tests/app.jet.ts", 43 | "dev": "node --watch --experimental-strip-types example/index.jet.ts", 44 | "node": "node --watch --experimental-strip-types example/index.jet.ts", 45 | "watch": "tsc tests/*.ts --target esnext --watch", 46 | "watch:docs": "docmach", 47 | "build": "npx docmach build", 48 | "lint": "gts lint", 49 | "clean": "gts clean", 50 | "fix": "gts fix", 51 | "prepare": "tsc --project tsconfig.d.json && tsc --project tsconfig.json && bun bundle.ts && npm run build", 52 | "pretest": "npm run build", 53 | "build:css": " css-purge -i docs/fragments/index.css -o docs/assets/index.css", 54 | "posttest": "npm run lint", 55 | "bench": "./bench.sh" 56 | }, 57 | "engines": { 58 | "node": ">=14.0.0", 59 | "bun": ">=0.1.0" 60 | }, 61 | "private": false, 62 | "devDependencies": { 63 | "@types/bun": "^1.1.8", 64 | "@types/node": "^22.7.5", 65 | "docmach": "^1.0.16", 66 | "gts": "^6.0.2", 67 | "mitata": "^1.0.34", 68 | "typescript": "^5.6.3" 69 | }, 70 | "docmach": { 71 | "docs-directory": "docs/docs", 72 | "assets-folder": "docs/assets", 73 | "build-directory": "docs/build" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /repository-open-graph-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeDynasty-dev/Jetpath/ec22ef8a7e056071590def7c0be943ed464398e7/repository-open-graph-template.png -------------------------------------------------------------------------------- /src/assets/api-doc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {NAME} API 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 414 | 415 | 416 | 417 |
418 |
419 | {NAME} Logo 420 |

{NAME} API Documentation

421 |
422 |
423 | 424 |
425 |
426 |

Project Information

427 |

{INFO}

428 |

Project Global Headers

429 |
430 |
431 |
432 | 433 |
434 |
435 |

API Endpoints

436 |
437 | 439 |
440 |
441 | 442 |
443 |
444 | 445 |
446 |
447 | © {CURRENT_YEAR} {NAME}. All rights reserved. Powered by Jetpath. 448 |
449 |
450 | 451 |
452 | 453 | 456 | 457 | 458 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { execSync, spawnSync } from "node:child_process"; 3 | import { rmSync } from "node:fs"; 4 | import { join } from "node:path"; 5 | 6 | const gitCheck = spawnSync("git", ["--version"], { stdio: "ignore" }); 7 | if (gitCheck.status !== 0) { 8 | console.error("Error: Git is not installed or not found in your PATH."); 9 | process.exit(1); 10 | } 11 | 12 | const [targetDir = "new-jetpath-project", branch = "main"] = process.argv.slice( 13 | 2, 14 | ); 15 | 16 | try { 17 | execSync( 18 | `git clone --depth 1 --branch ${branch} https://github.com/codedynasty-dev/jetpath-sample.git ${targetDir}`, 19 | { stdio: "inherit" }, 20 | ); 21 | console.log(` 22 | 🚀 Project created successfully! 23 | cd ${targetDir} 24 | npm install 25 | npm run dev 26 | `); 27 | } catch (err) { 28 | // @ts-ignore 29 | console.error("Error during git clone:", err.message); 30 | process.exit(1); 31 | } 32 | 33 | try { 34 | rmSync(join(targetDir, ".git"), { recursive: true, force: true }); 35 | } catch (err) { 36 | // @ts-ignore 37 | 38 | console.error("Warning: failed to remove .git folder:", err.message); 39 | } 40 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { writeFile } from "node:fs/promises"; 2 | import { sep } from "node:path"; 3 | import { 4 | _jet_middleware, 5 | _JetPath_paths, 6 | _JetPath_paths_trie, 7 | assignMiddleware, 8 | codeGen, 9 | compileAPI, 10 | compileUI, 11 | corsMiddleware, 12 | getHandlers, 13 | getLocalIP, 14 | server, 15 | } from "./primitives/functions.js"; 16 | import type { jetOptions, UnionToIntersection } from "./primitives/types.js"; 17 | import { JetPlugin, LOG } from "./primitives/classes.js"; 18 | 19 | export class Jetpath { 20 | public server: any; 21 | private listening: boolean = false; 22 | /** 23 | * an object you can set values to per request 24 | */ 25 | plugins: any[] = []; 26 | private options: jetOptions = { 27 | port: 8080, 28 | apiDoc: { display: "UI" }, 29 | cors: false, 30 | strictMode: "OFF", 31 | source: ".", 32 | }; 33 | private plugs: JetPlugin[] = []; 34 | constructor(options: jetOptions = {}) { 35 | Object.assign(this.options, options); 36 | if (!this.options.port) this.options.port = 8080; 37 | // ? setting up app configs 38 | if (this.options.cors === true) { 39 | corsMiddleware({ 40 | exposeHeaders: [], 41 | allowMethods: ["DELETE", "GET", "HEAD", "PATCH", "POST", "PUT"], 42 | origin: ["*"], 43 | allowHeaders: ["*"], 44 | maxAge: "86400", 45 | keepHeadersOnError: true, 46 | ...(typeof options?.cors === "object" ? options.cors : {}), 47 | }); 48 | } 49 | } 50 | derivePlugins< 51 | JetPluginTypes extends { 52 | executor: (init: any) => Record; 53 | server?: any; 54 | name: string; 55 | }[] = [], 56 | >( 57 | ...plugins: JetPluginTypes 58 | ) { 59 | if (this.listening) { 60 | throw new Error("Your app is listening new plugins can't be added."); 61 | } 62 | plugins.forEach((plugin) => { 63 | if ( 64 | typeof plugin.executor === "function" || typeof plugin.name === "string" 65 | ) { 66 | // ? add plugin to the server 67 | this.plugs.push( 68 | new JetPlugin(plugin), 69 | ); 70 | } else { 71 | throw new Error("Plugin executor and name is required"); 72 | } 73 | }); 74 | return this as unknown as 75 | & UnionToIntersection 76 | & Record; 77 | } 78 | async listen(): Promise { 79 | if (!this.options.source) { 80 | LOG.log( 81 | "Jetpath: Provide a source directory to avoid scanning the root directory", 82 | "warn", 83 | ); 84 | } 85 | // ? {-view-} here is replaced at build time to html 86 | let UI = `{{view}}`; 87 | LOG.log("Compiling...", "info"); 88 | const startTime = performance.now(); 89 | 90 | // ? Load all jetpath functions described in user code 91 | const errorsCount = await getHandlers(this.options?.source!, true); 92 | const endTime = performance.now(); 93 | // LOG.log("Compiled!"); 94 | //? compile API 95 | const [handlersCount, compiledAPI] = compileAPI(this.options); 96 | // ? render API in UI 97 | if (this.options?.apiDoc?.display === "UI") { 98 | UI = compileUI(UI, this.options, compiledAPI); 99 | const name = this.options?.apiDoc?.path || "/api-doc"; 100 | _JetPath_paths_trie["GET"].insert(name, ( 101 | ctx, 102 | ) => { 103 | if (this.options.apiDoc?.username && this.options.apiDoc?.password) { 104 | const authHeader = ctx.get("authorization"); 105 | if (authHeader && authHeader.startsWith("Basic ")) { 106 | const [authType, encodedToken] = authHeader.trim().split(" "); 107 | if (authType !== "Basic" || !encodedToken) { 108 | ctx.set( 109 | "WWW-Authenticate", 110 | `Basic realm=Jetpath API Doc`, 111 | ); 112 | ctx.send( 113 | `

Unauthorized

`, 114 | 401, 115 | "text/html", 116 | ); 117 | return; 118 | } 119 | let username, password; 120 | try { 121 | const decodedToken = new TextDecoder().decode( 122 | Uint8Array.from(atob(encodedToken), (c) => c.charCodeAt(0)), 123 | ); 124 | [username, password] = decodedToken.split(":"); 125 | } catch (error) { 126 | ctx.set( 127 | "WWW-Authenticate", 128 | `Basic realm=Jetpath API Doc`, 129 | ); 130 | ctx.send( 131 | `

Unauthorized

`, 132 | 401, 133 | "text/html", 134 | ); 135 | return; 136 | } 137 | if ( 138 | password === this.options?.apiDoc?.password && 139 | username === this.options?.apiDoc?.username 140 | ) { 141 | ctx.send(UI, 200, "text/html"); 142 | return; 143 | } else { 144 | ctx.set( 145 | "WWW-Authenticate", 146 | `Basic realm=Jetpath API Doc`, 147 | ); 148 | ctx.send( 149 | `

Unauthorized

`, 150 | 401, 151 | "text/html", 152 | ); 153 | return; 154 | } 155 | } else { 156 | ctx.set( 157 | "WWW-Authenticate", 158 | `Basic realm=Jetpath API Doc`, 159 | ); 160 | ctx.send( 161 | `

Unauthorized

`, 162 | 401, 163 | "text/html", 164 | ); 165 | return; 166 | } 167 | } else { 168 | ctx.send(UI, 200, "text/html"); 169 | return; 170 | } 171 | }); 172 | LOG.log( 173 | `Compiled ${handlersCount} Functions\nTime: ${ 174 | Math.round( 175 | endTime - startTime, 176 | ) 177 | }ms`, 178 | "info", 179 | ); 180 | //? generate types 181 | if (/(ON|WARN)/.test(this.options?.strictMode || "OFF")) { 182 | await codeGen( 183 | this.options.source || ".", 184 | this.options.strictMode as "ON" | "WARN", 185 | this.options.generatedRoutesFilePath, 186 | ); 187 | } 188 | LOG.log( 189 | `APIs: Viewable at http://localhost:${this.options.port}${ 190 | this.options?.apiDoc?.path || "/api-doc" 191 | }`, 192 | "info", 193 | ); 194 | } else if (this.options?.apiDoc?.display === "HTTP") { 195 | //? generate types 196 | await codeGen( 197 | this.options.source || ".", 198 | this.options?.strictMode as "ON" | "WARN", 199 | this.options.generatedRoutesFilePath, 200 | ); 201 | // ? render API in a .HTTP file 202 | await writeFile("api-doc.http", compiledAPI); 203 | LOG.log( 204 | `Compiled ${handlersCount} Functions\nTime: ${ 205 | Math.round( 206 | endTime - startTime, 207 | ) 208 | }ms`, 209 | "info", 210 | ); 211 | LOG.log( 212 | `APIs: written to ${sep}api-doc.http`, 213 | "info", 214 | ); 215 | } 216 | if (errorsCount) { 217 | for (let i = 0; i < errorsCount.length; i++) { 218 | LOG.log( 219 | `\nReport: ${errorsCount[i].file} file was not loaded due to \n "${ 220 | errorsCount[i].error 221 | }" error; \n please resolve!`, 222 | "warn", 223 | ); 224 | } 225 | } 226 | 227 | this.server = server(this.plugs, this.options); 228 | // 229 | assignMiddleware(_JetPath_paths, _jet_middleware); 230 | // ? start server 231 | this.listening = true; 232 | this.server.listen(this.options.port); 233 | LOG.log(`Open http://localhost:${this.options.port}`, "info"); 234 | // ? show external IP 235 | const localIP = getLocalIP(); 236 | if (localIP) { 237 | LOG.log(`External: http://${localIP}:${this.options.port}`, "info"); 238 | } 239 | } 240 | } 241 | 242 | //? exports 243 | export type { 244 | JetContext, 245 | JetFile, 246 | JetMiddleware, 247 | JetRoute, 248 | } from "./primitives/types.js"; 249 | export { JetServer } from "./primitives/classes.js"; 250 | export { use } from "./primitives/functions.js"; 251 | export { mime } from "./extracts/mimejs-extract.js"; 252 | -------------------------------------------------------------------------------- /src/primitives/types.ts: -------------------------------------------------------------------------------- 1 | import { IncomingMessage, Server, ServerResponse } from "node:http"; 2 | import type { _JetPath_paths, v } from "./functions.js"; 3 | import { type CookieOptions, SchemaBuilder } from "./classes.js"; 4 | import type { BunFile } from "bun"; 5 | import type Stream from "node:stream"; 6 | 7 | export type UnionToIntersection = 8 | (U extends any ? (x: U) => void : never) extends ( 9 | x: infer I, 10 | ) => void ? I 11 | : never; 12 | 13 | export interface JetContext< 14 | JetData extends { 15 | body?: Record; 16 | params?: Record; 17 | query?: Record; 18 | } = { body: {}; params: {}; query: {} }, 19 | JetPluginTypes extends Record[] = [], 20 | > { 21 | /** 22 | * an object you can set values to per request 23 | */ 24 | state: Record; 25 | /** 26 | * an object you can set values to per request 27 | */ 28 | plugins: UnionToIntersection & Record; 29 | /** 30 | * get body params after /? 31 | */ 32 | body: JetData["body"]; 33 | /** 34 | * get query params after /? 35 | */ 36 | query: JetData["query"]; 37 | /** 38 | * get route params in /:thing 39 | */ 40 | params: JetData["params"]; 41 | /** 42 | * websocket socket event class 43 | */ 44 | connection: jet_socket; 45 | /** 46 | * reply the request 47 | */ 48 | request: Request; 49 | /** 50 | * API status 51 | */ 52 | code: number; 53 | /** 54 | * send a stream 55 | * @param stream - The stream or file path to send 56 | * @param folder - The folder to save the stream to 57 | * @param ContentType - The content type of the stream 58 | * 59 | * PLEASE PROVIDE A VALID FOLDER PATH FOR SECURITY REASONS 60 | */ 61 | sendStream( 62 | stream: Stream | string | BunFile, 63 | folder?: string, 64 | ContentType?: string, 65 | ): void | never; 66 | /** 67 | * send a file for download 68 | * @param stream - The file path to send 69 | * @param folder - The folder to save the stream to 70 | * @param ContentType - The content type of the stream 71 | * 72 | * PLEASE PROVIDE A VALID FOLDER PATH FOR SECURITY REASONS 73 | */ 74 | download( 75 | stream: string | BunFile, 76 | folder?: string, 77 | ContentType?: string, 78 | ): void; 79 | /** 80 | * send a direct response 81 | * *Only for deno and bun 82 | */ 83 | sendResponse(response?: Response): void; 84 | /** 85 | * reply the request 86 | */ 87 | send(data: unknown, statusCode?: number, ContentType?: string): void; 88 | /** 89 | * redirect the request 90 | */ 91 | redirect(url: string): void; 92 | /** 93 | * get request header values 94 | */ 95 | get(field: string): string | undefined; 96 | /** 97 | * set request header values 98 | */ 99 | set(field: string, value: string): void; 100 | /** 101 | * Parses the request body 102 | */ 103 | getCookie(name: string): string | undefined; 104 | getCookies(): Record; 105 | setCookie(name: string, value: string, options: CookieOptions): void; 106 | clearCookie(name: string, options: CookieOptions): void; 107 | parse(options?: { 108 | maxBodySize?: number; 109 | contentType?: string; 110 | }): Promise; 111 | /** 112 | * Upgrade the request to a WebSocket connection 113 | */ 114 | upgrade(): void | never; 115 | /** 116 | * get original request 117 | */ 118 | path: string; 119 | payload?: string; 120 | _2?: Record; 121 | _3?: any; //Stream | undefined; // Stream 122 | _4?: boolean | undefined; 123 | _5?: JetRoute | undefined; 124 | _6?: Response | false; 125 | } 126 | 127 | export type JetPluginExecutorInitParams = { 128 | runtime: { 129 | node: boolean; 130 | bun: boolean; 131 | deno: boolean; 132 | }; 133 | server: Server; 134 | routesObject: typeof _JetPath_paths; 135 | JetPath_app: (req: Request) => Response; 136 | }; 137 | 138 | export type contentType = 139 | | "application/x-www-form-urlencoded" 140 | | "multipart/form-data" 141 | | "application/json"; 142 | 143 | export type methods = 144 | | "GET" 145 | | "POST" 146 | | "OPTIONS" 147 | | "DELETE" 148 | | "HEAD" 149 | | "PUT" 150 | | "CONNECT" 151 | | "TRACE" 152 | | "PATCH"; 153 | 154 | export type allowedMethods = methods[]; 155 | 156 | export type jetOptions = { 157 | /** 158 | * upgrade the request to a WebSocket connection 159 | */ 160 | upgrade?: boolean; 161 | /** 162 | * source of the app 163 | */ 164 | source?: string; 165 | /** 166 | * global headers 167 | */ 168 | globalHeaders?: Record; 169 | /** 170 | * strict mode 171 | */ 172 | strictMode?: "ON" | "OFF" | "WARN"; 173 | /** 174 | * generated routes file path 175 | * putting the file on the frontend folder will make it accessible 176 | * during build time 177 | * @default generates nothing 178 | */ 179 | generatedRoutesFilePath?: string; 180 | /** 181 | * keep alive timeout 182 | */ 183 | keepAliveTimeout?: number; 184 | /** 185 | * api documentation options 186 | */ 187 | apiDoc?: { 188 | display?: "UI" | "HTTP" | false; 189 | environments?: Record; 190 | name?: string; 191 | info?: string; 192 | color?: string; 193 | logo?: string; 194 | path?: string; 195 | password?: string; 196 | username?: string; 197 | }; 198 | /** 199 | * credentials options 200 | */ 201 | credentials?: { 202 | cert: string; 203 | key: string; 204 | }; 205 | /** 206 | * port 207 | */ 208 | port?: number; 209 | /** 210 | * cors options 211 | */ 212 | cors?: 213 | | { 214 | allowMethods?: allowedMethods; 215 | secureContext?: { 216 | "Cross-Origin-Opener-Policy": 217 | | "same-origin" 218 | | "unsafe-none" 219 | | "same-origin-allow-popups"; 220 | "Cross-Origin-Embedder-Policy": "require-corp" | "unsafe-none"; 221 | }; 222 | allowHeaders?: string[]; 223 | exposeHeaders?: string[]; 224 | keepHeadersOnError?: boolean; 225 | maxAge?: string; 226 | credentials?: boolean; 227 | privateNetworkAccess?: any; 228 | origin?: string[]; 229 | } 230 | | boolean; 231 | }; 232 | 233 | export type HTTPBody> = { 234 | [x in keyof Obj]: { 235 | err?: string; 236 | type?: 237 | | "string" 238 | | "number" 239 | | "file" 240 | | "object" 241 | | "boolean" 242 | | "array" 243 | | "date"; 244 | arrayType?: 245 | | "string" 246 | | "number" 247 | | "file" 248 | | "object" 249 | | "boolean" 250 | | "array" 251 | | "date"; 252 | RegExp?: RegExp; 253 | inputAccept?: string; 254 | inputType?: 255 | | "date" 256 | | "email" 257 | | "file" 258 | | "password" 259 | | "number" 260 | | "time" 261 | | "tel" 262 | | "datetime" 263 | | "url"; 264 | inputDefaultValue?: string | number | boolean; 265 | required?: boolean; 266 | validator?: (value: any) => boolean | string; 267 | objectSchema?: HTTPBody>; 268 | }; 269 | }; 270 | 271 | export type JetMiddleware< 272 | JetData extends { 273 | body?: Record; 274 | params?: Record; 275 | query?: Record; 276 | } = { body: {}; params: {}; query: {} }, 277 | JetPluginTypes extends Record[] = [], 278 | > = ( 279 | ctx: JetContext, 280 | ) => 281 | | void 282 | | Promise 283 | | (( 284 | ctx: JetContext, 285 | error: unknown, 286 | ) => void | Promise) 287 | | Promise< 288 | (( 289 | ctx: JetContext, 290 | error: unknown, 291 | ) => void | Promise) | undefined 292 | > 293 | | undefined; 294 | 295 | export type JetRoute< 296 | JetData extends { 297 | body?: Record; 298 | params?: Record; 299 | query?: Record; 300 | response?: Record; 301 | } = { 302 | body: {}; 303 | params: {}; 304 | query: {}; 305 | response: {}; 306 | title: ""; 307 | description: ""; 308 | method: ""; 309 | path: ""; 310 | jet_middleware: []; 311 | }, 312 | JetPluginTypes extends Record[] = [], 313 | > = { 314 | (ctx: JetContext): Promise | void; 315 | body?: HTTPBody>; 316 | headers?: Record; 317 | title?: string; 318 | description?: string; 319 | method?: string; 320 | path?: string; 321 | jet_middleware?: JetMiddleware[]; 322 | params?: HTTPBody>; 323 | query?: HTTPBody>; 324 | response?: HTTPBody>; 325 | }; 326 | 327 | interface jet_socket { 328 | addEventListener( 329 | event: "message" | "close" | "drain" | "open", 330 | listener: (socket: WebSocket, ...params: any[]) => void, 331 | ): void; 332 | } 333 | 334 | export type JetFile = { 335 | fileName: string; 336 | content: Uint8Array; 337 | mimeType: string; 338 | }; 339 | 340 | export type SchemaType = 341 | | "string" 342 | | "number" 343 | | "boolean" 344 | | "array" 345 | | "object" 346 | | "date" 347 | | "file"; 348 | 349 | export interface ValidationOptions { 350 | err?: string; 351 | RegExp?: RegExp; 352 | validator?: (value: any) => boolean | string; 353 | inputDefaultValue?: any; 354 | required?: boolean; 355 | } 356 | export interface FileOptions { 357 | inputAccept?: string; 358 | inputMultiple?: boolean; 359 | err?: string; 360 | } 361 | 362 | export interface ArrayOptions extends ValidationOptions { 363 | arrayType?: SchemaType | "object"; 364 | objectSchema?: HTTPBody; 365 | } 366 | 367 | export interface ObjectOptions extends ValidationOptions { 368 | objectSchema?: HTTPBody; 369 | } 370 | 371 | export type SchemaDefinition = 372 | & { 373 | type: SchemaType; 374 | } 375 | & ValidationOptions 376 | & ArrayOptions 377 | & ObjectOptions; 378 | 379 | export type compilerType< 380 | JetData extends { 381 | body?: Record; 382 | params?: Record; 383 | query?: Record; 384 | response?: Record; 385 | }, 386 | JetPluginTypes extends Record[] = [], 387 | > = { 388 | //? docs and validation 389 | /** 390 | * Sets the API body validation and documentation body for the endpoint 391 | */ 392 | body: ( 393 | schemaFn: ( 394 | t: typeof v, 395 | ) => Partial< 396 | Record>, SchemaBuilder> 397 | >, 398 | ) => compilerType; 399 | //? docs and validation 400 | /** 401 | * Sets the API documentation query for the endpoint 402 | */ 403 | query: ( 404 | schemaFn: ( 405 | t: typeof v, 406 | ) => Partial< 407 | Record>, SchemaBuilder> 408 | >, 409 | ) => compilerType; 410 | //? docs only 411 | /** 412 | * Sets the API documentation title for the endpoint 413 | */ 414 | title: (title: string) => compilerType; 415 | //? docs only 416 | /** 417 | * Sets the API documentation description for the endpoint 418 | */ 419 | description: (description: string) => compilerType; 420 | }; 421 | -------------------------------------------------------------------------------- /tsconfig.d.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2022", "esnext", "dom", "dom.iterable"], 4 | "target": "ESNext", 5 | "module": "NodeNext", 6 | "moduleDetection": "force", 7 | "verbatimModuleSyntax": true, 8 | "declaration": true, 9 | "removeComments": false, 10 | "strict": true, 11 | "skipLibCheck": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "useUnknownInCatchVariables": true, 16 | "noPropertyAccessFromIndexSignature": true, 17 | "outDir": "./dist" 18 | }, 19 | "include": ["src/**/*" 20 | ], 21 | "exclude": ["node_modules", "src/assets", "src/__tests__", "src/__mocks__"], 22 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2022", "esnext", "dom", "dom.iterable"], 4 | "target": "ESNext", 5 | "module": "NodeNext", 6 | "moduleDetection": "force", 7 | "verbatimModuleSyntax": true, 8 | "noEmit": false, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "declaration": true, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "useUnknownInCatchVariables": true, 16 | "noPropertyAccessFromIndexSignature": true, 17 | "outDir": "./dist" 18 | }, 19 | "include": ["src/**/*" 20 | ], 21 | "exclude": ["node_modules","src/assets","src/typings","src/__tests__","src/__mocks__","src/__fixtures__"], 22 | } --------------------------------------------------------------------------------