+ Our main goal and pride at Leaf is to make PHP development as simple and elegant as possible.
+
+
+ Our team is always looking to improve your experience using the Leaf framework and it's ecosystem of tools. You
+ can follow along as our team discusses it's insights from the past year and what's to come in 2023.
+
+ You can read our blog posts at
+ blog.leafphp.dev. You may also go social
+ at
+ Twitter, or join our
+ discussions or
+ watch our videos on
+ YouTube.
+
+
+ You can read our blog posts at
+ blog.leafphp.dev. You may also go social
+ at
+ Twitter, or join our
+ discussions or
+ watch our videos on
+ YouTube.
+
+
+
+
+
diff --git a/apps/docs/.vitepress/theme/components/preferences.ts b/apps/docs/.vitepress/theme/components/preferences.ts
new file mode 100644
index 0000000..eb3e4b3
--- /dev/null
+++ b/apps/docs/.vitepress/theme/components/preferences.ts
@@ -0,0 +1,18 @@
+import { Header } from 'vitepress'
+import { ref } from 'vue'
+
+const hasStorage = typeof localStorage !== 'undefined'
+const get = (key: string, defaultValue = false): boolean =>
+ hasStorage
+ ? JSON.parse(localStorage.getItem(key) || String(defaultValue))
+ : defaultValue
+
+export const preferFunctionalKey = 'vue-docs-prefer-functional'
+export const preferFunctional = ref(get(preferFunctionalKey))
+
+export const preferSFCKey = 'vue-docs-prefer-sfc'
+export const preferSFC = ref(get(preferSFCKey, true))
+
+export function filterHeadersByPreference(h: Header) {
+ return preferFunctional.value ? !h.optionsOnly : !h.compositionOnly
+}
diff --git a/apps/docs/.vitepress/theme/index.ts b/apps/docs/.vitepress/theme/index.ts
new file mode 100644
index 0000000..5387e63
--- /dev/null
+++ b/apps/docs/.vitepress/theme/index.ts
@@ -0,0 +1,32 @@
+import './styles/index.css'
+import { h, App } from 'vue'
+import { VPTheme } from '@leafphp/docs-theme'
+import PreferenceSwitch from './components/PreferenceSwitch.vue'
+import {
+ preferFunctional,
+ preferSFC,
+ filterHeadersByPreference
+} from './components/preferences'
+import SponsorsAside from './components/SponsorsAside.vue'
+import VueSchoolLink from './components/VueSchoolLink.vue'
+// import VueJobs from './components/VueJobs.vue'
+// import Banner from './components/Banner.vue'
+
+export default Object.assign({}, VPTheme, {
+ Layout: () => {
+ return h(VPTheme.Layout, null, {
+ // banner: () => h('div', {}, [
+ // h(Banner),
+ // ]),
+ 'sidebar-top': () => h(PreferenceSwitch),
+ 'aside-mid': () => h(SponsorsAside),
+ // 'aside-bottom': () => h(VueJobs)
+ })
+ },
+ enhanceApp({ app }: { app: App }) {
+ app.provide('prefer-functional', preferFunctional)
+ app.provide('prefer-sfc', preferSFC)
+ app.provide('filter-headers', filterHeadersByPreference)
+ app.component('VueSchoolLink', VueSchoolLink)
+ }
+})
diff --git a/apps/docs/.vitepress/theme/styles/badges.css b/apps/docs/.vitepress/theme/styles/badges.css
new file mode 100644
index 0000000..df66ff6
--- /dev/null
+++ b/apps/docs/.vitepress/theme/styles/badges.css
@@ -0,0 +1,28 @@
+.vt-badge.wip:before {
+ content: 'WIP';
+}
+
+.vt-badge.ts {
+ background-color: #3178c6;
+}
+.vt-badge.ts:before {
+ content: 'TS';
+}
+
+.vt-badge.dev-only,
+.vt-badge.experimental {
+ color: var(--vt-c-text-light-1);
+ background-color: var(--vt-c-yellow);
+}
+
+.vt-badge.dev-only:before {
+ content: 'Dev only';
+}
+
+.vt-badge.experimental:before {
+ content: 'Experimental';
+}
+
+.vt-badge[data-text]:before {
+ content: attr(data-text);
+}
diff --git a/apps/docs/.vitepress/theme/styles/index.css b/apps/docs/.vitepress/theme/styles/index.css
new file mode 100644
index 0000000..2632413
--- /dev/null
+++ b/apps/docs/.vitepress/theme/styles/index.css
@@ -0,0 +1,6 @@
+@import "./pages.css";
+@import "./badges.css";
+@import "./options-boxes.css";
+@import "./inline-demo.css";
+@import "./utilities.css";
+@import "./style-guide.css";
diff --git a/apps/docs/.vitepress/theme/styles/inline-demo.css b/apps/docs/.vitepress/theme/styles/inline-demo.css
new file mode 100644
index 0000000..02bded8
--- /dev/null
+++ b/apps/docs/.vitepress/theme/styles/inline-demo.css
@@ -0,0 +1,90 @@
+.vt-doc a[href^="https://sfc.vuejs.org"]:before
+{
+ content: '▶';
+ width: 20px;
+ height: 20px;
+ display: inline-block;
+ border-radius: 10px;
+ vertical-align: middle;
+ position: relative;
+ top: -2px;
+ color: var(--vt-c-green);
+ border: 2px solid var(--vt-c-green);
+ margin-right: 8px;
+ margin-left: 4px;
+ line-height: 15px;
+ padding-left: 4.5px;
+ font-size: 11px;
+}
+
+.demo {
+ padding: 22px 24px;
+ border-radius: 8px;
+ box-shadow: var(--vt-shadow-2);
+ margin-bottom: 1.2em;
+ transition: background-color 0.5s ease;
+}
+
+.dark .demo {
+ background-color: var(--vt-c-bg-soft);
+}
+
+.demo p {
+ margin: 0;
+}
+
+.demo button {
+ background-color: var(--vt-c-bg-mute);
+ transition: background-color 0.5s;
+ padding: 5px 12px;
+ border: 1px solid var(--vt-c-divider);
+ border-radius: 8px;
+ font-size: 0.9em;
+ font-weight: 600;
+}
+
+.demo button + button {
+ margin-left: 1em;
+}
+
+.demo input,
+.demo textarea,
+.demo select {
+ border: 1px solid var(--vt-c-divider);
+ border-radius: 4px;
+ padding: 0.2em 0.6em;
+ margin-top: 10px;
+ background: transparent;
+ transition: background-color 0.5s;
+}
+
+.dark .demo select {
+ background: var(--vt-c-bg-soft);
+}
+
+.dark .demo select option {
+ background: transparent;
+}
+
+.demo input:not([type]):focus,
+.demo textarea:focus,
+.demo select:focus {
+ outline: 1px solid blue;
+}
+
+.demo select {
+ /* this was set by normalize.css */
+ -webkit-appearance: listbox;
+}
+
+.demo label {
+ margin: 0 1em 0 0.4em;
+}
+
+.demo select[multiple] {
+ width: 100px;
+}
+
+.demo h1 {
+ margin: 10px 0 0;
+}
diff --git a/apps/docs/.vitepress/theme/styles/options-boxes.css b/apps/docs/.vitepress/theme/styles/options-boxes.css
new file mode 100644
index 0000000..69e6296
--- /dev/null
+++ b/apps/docs/.vitepress/theme/styles/options-boxes.css
@@ -0,0 +1,27 @@
+.next-steps {
+ margin-top: 3rem;
+}
+
+.next-steps .vt-box {
+ border: 1px solid var(--vt-c-bg-soft);
+}
+
+.next-steps .vt-box:hover {
+ border-color: var(--vt-c-green-light);
+ transition: border-color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
+}
+
+.vt-doc .next-steps-link {
+ font-size: 20px;
+ line-height: 1.4;
+ letter-spacing: -0.02em;
+ margin-bottom: 0.75em;
+ display: block;
+ color: var(--vt-c-green);
+}
+
+.vt-doc .next-steps-caption {
+ margin-bottom: 0;
+ color: var(--vt-c-text-2);
+ transition: color 0.5s;
+}
diff --git a/apps/docs/.vitepress/theme/styles/pages.css b/apps/docs/.vitepress/theme/styles/pages.css
new file mode 100644
index 0000000..2d807ec
--- /dev/null
+++ b/apps/docs/.vitepress/theme/styles/pages.css
@@ -0,0 +1,34 @@
+/* always show anchors on /api/ and /style-guide/ pages */
+.vt-doc.api h2 .header-anchor,
+.vt-doc.style-guide h2 .header-anchor {
+ opacity: 1;
+}
+
+.vt-doc.sponsor h3 {
+ text-align: center;
+ padding-bottom: 1em;
+ border-bottom: 1px solid var(--vt-c-divider-light);
+}
+
+.vt-doc.sponsor h3 .header-anchor {
+ display: none;
+}
+
+.vt-grid-list {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ grid-gap: 1rem;
+}
+
+.dark .custom-block [class*='language-'] pre {
+ background-color: var(--vt-c-theme-plain);
+}
+
+.dark .custom-block .custom-block {
+ background-color: var(--vt-c-theme-plain) !important;
+ border-left: none !important;
+}
+
+.details.custom-block::before {
+ content: none !important;
+}
diff --git a/apps/docs/.vitepress/theme/styles/style-guide.css b/apps/docs/.vitepress/theme/styles/style-guide.css
new file mode 100644
index 0000000..0d249c8
--- /dev/null
+++ b/apps/docs/.vitepress/theme/styles/style-guide.css
@@ -0,0 +1,65 @@
+.style-example {
+ border-radius: 8px 8px 12px 12px;
+ margin: 1.6em 0;
+ padding: 1.6em 1.6em 0.1px;
+ position: relative;
+ border: 1px solid transparent;
+ transition: background-color 0.25s ease, border-color 0.25s ease;
+}
+
+.vt-doc .style-example h3 {
+ margin: 0;
+ font-size: 1.1em;
+}
+
+.style-example-bad {
+ background: #f7e8e8;
+}
+.dark .style-example-bad {
+ background: transparent;
+ border-color: var(--vt-c-red);
+}
+
+.style-example-bad h3 {
+ color: var(--vt-c-red);
+}
+
+.style-example-good {
+ background: #ecfaf7;
+}
+.dark .style-example-good {
+ background: transparent;
+ border-color: var(--vt-c-green);
+}
+
+.style-example-good h3 {
+ color: var(--vt-c-green);
+}
+
+.details summary {
+ font-weight: bold !important;
+}
+
+.style-verb {
+ font-size: 0.6em;
+ display: inline-block;
+ border-radius: 6px;
+ font-size: 0.65em;
+ line-height: 1;
+ font-weight: 600;
+ padding: 0.35em 0.4em 0.3em;
+ position: relative;
+ top: -0.15em;
+ margin-right: 0.5em;
+ color: var(--vt-c-bg);
+ transition: color 0.5s;
+ background-color: var(--vt-c-brand);
+}
+
+.style-verb.avoid {
+ background-color: var(--vt-c-red);
+}
+
+div[class~=language-php] .token.variable {
+ color: #fff !important;
+}
diff --git a/apps/docs/.vitepress/theme/styles/utilities.css b/apps/docs/.vitepress/theme/styles/utilities.css
new file mode 100644
index 0000000..f9e054b
--- /dev/null
+++ b/apps/docs/.vitepress/theme/styles/utilities.css
@@ -0,0 +1,14 @@
+.nowrap {
+ white-space: nowrap;
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+}
diff --git a/apps/docs/.vitepress/theme/styles/vue-mastery.css b/apps/docs/.vitepress/theme/styles/vue-mastery.css
new file mode 100644
index 0000000..34f2bd6
--- /dev/null
+++ b/apps/docs/.vitepress/theme/styles/vue-mastery.css
@@ -0,0 +1,65 @@
+.vue-mastery-link {
+ background-color: var(--vt-c-bg-soft);
+ border-radius: 8px;
+ padding: 8px 16px 8px 8px;
+ transition: color 0.5s, background-color 0.5s;
+}
+
+.vue-mastery-link a {
+ display: flex;
+ align-items: center;
+}
+
+.vue-mastery-link .banner {
+ background-color: var(--vt-c-white-soft);
+ border-radius: 4px;
+ width:96px;
+ height:56px;
+ object-fit: cover;
+}
+
+.vue-mastery-link .description {
+ flex: 1;
+ font-weight: 500;
+ font-size: 14px;
+ line-height: 20px;
+ color: var(--vt-c-text-1);
+ margin: 0 0 0 16px;
+ transition: color 0.5s;
+}
+
+.vue-mastery-link .description span {
+ color: var(--vt-c-brand);
+}
+
+.vue-mastery-link .logo-wrapper {
+ position: relative;
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ background-color: var(--vt-c-white);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.vue-mastery-link .logo-wrapper img {
+ width: 25px;
+ object-fit: contain;
+}
+
+@media (max-width: 576px) {
+ .vue-mastery-link .banner {
+ width:56px;
+ }
+
+ .vue-mastery-link .description {
+ font-size: 12px;
+ line-height: 18px;
+ }
+ .vue-mastery-link .logo-wrapper {
+ position: relative;
+ width: 32px;
+ height: 32px;
+ }
+}
diff --git a/apps/docs/LICENSE b/apps/docs/LICENSE
new file mode 100644
index 0000000..55b818a
--- /dev/null
+++ b/apps/docs/LICENSE
@@ -0,0 +1,29 @@
+MIT License
+
+Copyright (c) 2023 Michael Darko-Duodu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+--------------------------------------------------------------------------------
+Third party licenses are below
+--------------------------------------------------------------------------------
+
+Parts of this project's code was based on code from the below repositories:
+
+- LICENSE.VUEJS https://github.com/vuejs/docs
diff --git a/apps/docs/LICENSE.VUEJS b/apps/docs/LICENSE.VUEJS
new file mode 100644
index 0000000..8944fd0
--- /dev/null
+++ b/apps/docs/LICENSE.VUEJS
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 vuejs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/apps/docs/README.md b/apps/docs/README.md
new file mode 100644
index 0000000..e35957f
--- /dev/null
+++ b/apps/docs/README.md
@@ -0,0 +1,20 @@
+# Leaf 3
+
+## Contributing
+
+This site is built with [VitePress](https://github.com/vuejs/vitepress) and depends on [@leafphp/docs-theme](https://github.com/leafsphp/leaf-docs-theme). Site content is written in Markdown format located in `src`. For simple edits, you can directly edit the file on GitHub and generate a Pull Request.
+
+For local development, [pnpm](https://pnpm.io/) is preferred as package manager:
+
+```bash
+pnpm i
+pnpm run dev
+```
+
+This project requires Node.js to be `v14.0.0` or higher, because we use new JavaScript features in our code, such as optional chaining.
+
+## Working on the content
+
+- See VitePress docs on supported [Markdown Extensions](https://vitepress.vuejs.org/guide/markdown.html) and the ability to [use Vue syntax inside markdown](https://vitepress.vuejs.org/guide/using-vue.html).
+
+- See the [Writing Guide](http://localhost:3000/community/contributing/writing-guide.html) for our rules and recommendations on writing and maintaining documentation content.
diff --git a/apps/docs/env.d.ts b/apps/docs/env.d.ts
new file mode 100644
index 0000000..615c851
--- /dev/null
+++ b/apps/docs/env.d.ts
@@ -0,0 +1,13 @@
+///
+///
+
+declare module '@leafphp/docs-theme/config' {
+ import { UserConfig } from 'vitepress'
+ const config: () => Promise
+ export default config
+}
+
+declare module '@leafphp/docs-theme/highlight' {
+ const createHighlighter: () => Promise<(input: string) => string>
+ export default createHighlighter
+}
diff --git a/apps/docs/netlify.toml b/apps/docs/netlify.toml
new file mode 100644
index 0000000..8690fdf
--- /dev/null
+++ b/apps/docs/netlify.toml
@@ -0,0 +1,11 @@
+[[redirects]]
+ from = "/aloe-cli/"
+ to = "/docs/mvc/console"
+
+[build.environment]
+ NODE_VERSION = "16"
+ NPM_FLAGS = "--version" # prevent Netlify npm install
+
+[build]
+ publish = ".vitepress/dist"
+ command = "npx pnpm i --store=node_modules/.pnpm-store && npm run build"
diff --git a/apps/docs/package.json b/apps/docs/package.json
new file mode 100644
index 0000000..7b38243
--- /dev/null
+++ b/apps/docs/package.json
@@ -0,0 +1,37 @@
+{
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "scripts": {
+ "dev": "vitepress",
+ "build": "vitepress build",
+ "serve": "vitepress serve",
+ "preinstall": "npx only-allow pnpm"
+ },
+ "dependencies": {
+ "449.css": "^1.3.0",
+ "@codemirror/lang-php": "^6.0.0",
+ "@leafphp/docs-theme": "0.1.2",
+ "@vue/repl": "^1.2.4",
+ "axios": "^0.27.2",
+ "dynamics.js": "^1.1.5",
+ "gsap": "^3.9.0",
+ "vitepress": "^0.22.4",
+ "vue": "^3.2.37"
+ },
+ "devDependencies": {
+ "@types/markdown-it": "^12.2.3",
+ "@types/node": "^16.9.1"
+ },
+ "pnpm": {
+ "peerDependencyRules": {
+ "ignoreMissing": [
+ "@algolia/client-search",
+ "react",
+ "react-dom",
+ "@types/react",
+ "search-insights"
+ ]
+ }
+ }
+}
diff --git a/apps/docs/src/aloe-cli/index.md b/apps/docs/src/aloe-cli/index.md
new file mode 100755
index 0000000..d9eb856
--- /dev/null
+++ b/apps/docs/src/aloe-cli/index.md
@@ -0,0 +1,60 @@
+# Aloe CLI
+
+Aloe is a simple but powerful console service that makes building your leaf apps just a simple walk in the park. Aloe CLI ships with the default Leaf console tool in the newer versions of Leaf API, Leaf MVC and Skeleton.
+
+Aloe comes with a predefined set of commands which provide project scaffolding, database and app management right from the console. It also introduces a much simpler and cleaner way to write your commands.
+
+## Aloe List
+
+```bash
+ALOE
+
+Usage:
+ command [options] [arguments]
+
+Options:
+ -h, --help Display this help message
+ -q, --quiet Do not output any message
+ -V, --version Display this application version
+ --ansi Force ANSI output
+ --no-ansi Disable ANSI output
+ -n, --no-interaction Do not ask any interactive question
+ -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
+
+Available commands:
+ example example command's description
+ help Displays help for a command
+ interact Interact with your application
+ list Lists commands
+ serve Start the leaf development server
+ aloe
+ aloe:config Install aloe config
+ app
+ app:down Place app in maintenance mode
+ app:up Remove app from maintenance mode
+ d
+ d:command Delete a console command
+ d:controller Delete a controller
+ d:factory Delete a model factory
+ d:migration Delete a migration
+ d:model Delete a model
+ d:seed Delete a model seeder
+ db
+ db:install Create new database from .env variables
+ db:migrate Run the database migrations
+ db:rollback Rollback all database migrations
+ db:seed Seed the database with records
+ env
+ env:generate Generate .env file
+ g
+ g:command Create a new console command
+ g:controller Create a new controller class
+ g:factory Create a new model factory
+ g:helper Create a new helper class
+ g:migration Create a new migration file
+ g:model Create a new model class
+ g:seed Create a new seed file
+ g:template Create a new view file
+ scaffold
+ scaffold:auth Scaffold basic app authentication
+```
diff --git a/apps/docs/src/aloe-cli/v/1.2.3/commands/custom.md b/apps/docs/src/aloe-cli/v/1.2.3/commands/custom.md
new file mode 100755
index 0000000..14c45a3
--- /dev/null
+++ b/apps/docs/src/aloe-cli/v/1.2.3/commands/custom.md
@@ -0,0 +1,107 @@
+# Aloe CLI: Custom Commands
+
+This section talks about creating custom commands using Aloe CLI. These commands would be called through the CLI.
+
+To get started, aloe CLI provides `g:command` which scaffolds a basic command for you and even registers it, so you can go straight into working on the command's logic. The new command will be created in the default commands directory.
+
+The default directory for commands for Leaf API and Leaf MVC is `App\Console`, with skeleton, you're free to decide where to place your commands.
+
+```bash
+php leaf g:command SendMail
+```
+
+Aloe can also generate namespaced commands directly for you. You don't have to manually set namespaces as done with other CLI tools.
+
+```bash
+php leaf g:command mail:send
+```
+
+If you want to, you can even generate the command by it's name instead of it's class. Aloe is smart enough to differentiate them.
+
+```bash
+php leaf g:command shutdown
+```
+
+## Command Structure
+
+After generating your command, you should start writing what to execute once the command is called. Aloe smartly generates a command name for you, even if you create the command using the class name, however, if it doesn't match what you need, you can always change it.
+
+With the `mail:send` example above, Aloe wil generate `App\Console\MailSendCommand`, in this file, we'll have something that looks like this:
+
+```php
+comment("mail:send command's output");
+ }
+}
+```
+
+We can add an argument to find the user to send the email to, and output a message while sending the email.
+These can be done in the `config` and `handle` methods respectively. [Read more](/aloe-cli/v/1.2.3/commands/io) here.
+
+```php
+public function config()
+{
+ $this->setArgument("user", "required");
+}
+
+public function handle()
+{
+ $user = $this->argument('user');
+
+ $this->comment("Sending email to $user");
+
+ $success = CustomEmailHandler::send($user);
+
+ if ($success) {
+ $this->info("Email sent successfully");
+ } else {
+ $this->error("Couldn't send email, pls try again");
+ }
+}
+```
+
+## Registering Commands
+
+By default, aloe cli registers all commands generated, however, if you have a command you want to register manually, or commands from a package which need to use Aloe, you can also add them pretty easily.
+
+Simply locate the `leaf` file in the root directory of your project, open it up and find a commented section talking about custom commands.
+
+```php
+/*
+|--------------------------------------------------------------------------
+| Add custom command
+|--------------------------------------------------------------------------
+|
+| If you have a new command to add to Leaf
+|
+*/
+$console->register(\App\Console\ExampleCommand::class);
+```
+
+An example command has already been registered, so you can follow this example. Simply call the `register` method. You can also pass in an array of commands to register, as such, a custom package with a couple of commands to register can simply return an array of all those commands.
+
+```php
+$console->register([
+ \App\Console\AppCommand::class,
+ CustomPackage::commands(),
+]);
+```
+
+## Next Steps
+
+- [Commands IO](/aloe-cli/v/1.2.3/commands/io)
+- [libraries](/aloe-cli/v/1.2.3/libraries)
+- [G Commands](/aloe-cli/v/1.2.3/commands/g-commands)
+- [DB commands](/aloe-cli/v/1.2.3/commands/db-commands)
diff --git a/apps/docs/src/aloe-cli/v/1.2.3/commands/d-commands.md b/apps/docs/src/aloe-cli/v/1.2.3/commands/d-commands.md
new file mode 100755
index 0000000..251169d
--- /dev/null
+++ b/apps/docs/src/aloe-cli/v/1.2.3/commands/d-commands.md
@@ -0,0 +1,90 @@
+# Aloe CLI: d commands
+
+d technically stands for delete. Aloe CLI d commands technically delete some sort of file or class.
+
+## d:command
+
+This command is used to delete and unregister a custom command
+
+```bash
+Description:
+ Delete a console command
+
+Usage:
+ d:command
+
+Arguments:
+ file The name of the console file
+```
+
+## d:controller
+
+```bash
+Description:
+ Delete a controller
+
+Usage:
+ d:controller
+
+Arguments:
+ controller controller name
+```
+
+## d:factory
+
+```bash
+Description:
+ Delete a model factory
+
+Usage:
+ d:factory
+
+Arguments:
+ factory factory name
+```
+
+## d:migration
+
+```bash
+Description:
+ Delete a migration
+
+Usage:
+ d:migration
+
+Arguments:
+ file File to delete
+```
+
+## d:model
+
+```bash
+Description:
+ Delete a model
+
+Usage:
+ d:model
+
+Arguments:
+ model model name
+```
+
+## d:seed
+
+```bash
+Description:
+ Delete a model seeder
+
+Usage:
+ d:seed
+
+Arguments:
+ seed seeder name
+```
+
+## Next Steps
+
+- [DB Commands](/aloe-cli/v/1.2.3/commands/db-commands)
+- [Custom commands](/aloe-cli/v/1.2.3/commands/custom)
+- [Commands IO](/aloe-cli/v/1.2.3/commands/io)
+- [Creating Libraries](/aloe-cli/v/1.2.3/libraries)
diff --git a/apps/docs/src/aloe-cli/v/1.2.3/commands/db-commands.md b/apps/docs/src/aloe-cli/v/1.2.3/commands/db-commands.md
new file mode 100755
index 0000000..f5852a9
--- /dev/null
+++ b/apps/docs/src/aloe-cli/v/1.2.3/commands/db-commands.md
@@ -0,0 +1,61 @@
+# Aloe CLI: DB Commands
+
+These commands help you manage and interact with your database.
+
+## db:install
+
+Create the database in your .env variables if it doesn't already exist.
+
+```bash
+Description:
+ Create new database from .env variables
+
+Usage:
+ db:install
+```
+
+## db:migrate
+
+```bash
+Description:
+ Run the database migrations
+
+Usage:
+ db:migrate [options]
+
+Options:
+ -f, --file[=FILE] Rollback a particular file
+ -s, --seed Run seeds after migration
+```
+
+## db:rollback
+
+```bash
+Description:
+ Rollback database migrations
+
+Usage:
+ db:rollback [options]
+
+Options:
+ -s, --step[=STEP] The batch to rollback [default: "all"]
+ -f, --file[=FILE] Rollback a particular file
+```
+
+Dont use -f and -s together
+
+## db:seed
+
+```bash
+Description:
+ Seed the database with records
+
+Usage:
+ db:seed
+```
+
+## Next Steps
+
+- [Custom commands](/aloe-cli/v/1.2.3/commands/custom)
+- [Commands IO](/aloe-cli/v/1.2.3/commands/io)
+- [Creating Libraries](/aloe-cli/v/1.2.3/libraries)
diff --git a/apps/docs/src/aloe-cli/v/1.2.3/commands/g-commands.md b/apps/docs/src/aloe-cli/v/1.2.3/commands/g-commands.md
new file mode 100755
index 0000000..6466a0b
--- /dev/null
+++ b/apps/docs/src/aloe-cli/v/1.2.3/commands/g-commands.md
@@ -0,0 +1,117 @@
+# Aloe CLI: g commands
+
+These commands generate something.
+
+## g:command
+
+```bash
+Description:
+ Create a new console command
+
+Usage:
+ g:command
+
+Arguments:
+ consoleCommand command name
+```
+
+## g:controller
+
+```bash
+Description:
+ Create a new controller class
+
+Usage:
+ g:controller [options] [--]
+
+Arguments:
+ controller controller name
+
+Options:
+ -a, --all Create a model and migration for controller
+ -m, --model Create a model for controller
+ -r, --resource Create a resource controller
+ -w, --web Create a web(ordinary) controller
+ --api Create a web(ordinary) controller
+ -ar, --api-resource Create an API resource controller
+ -wr, --web-resource Create a web resource controller
+```
+
+## g:factory
+
+```bash
+Description:
+ Create a new model factory
+
+Usage:
+ g:factory
+
+Arguments:
+ factory factory name
+```
+
+## g:helper
+
+```bash
+Description:
+ Create a new helper class
+
+Usage:
+ g:helper
+
+Arguments:
+ helper helper name
+```
+
+## g:migration
+
+```bash
+Description:
+ Create a new migration file
+
+Usage:
+ g:migration
+
+Arguments:
+ migration migration file name
+```
+
+## g:model
+
+```bash
+Description:
+ Create a new model class
+
+Usage:
+ g:model [options] [--]
+
+Arguments:
+ model model file name
+
+Options:
+ -m, --migration Create a migration for model
+```
+
+## g:seed
+
+```bash
+Description:
+ Create a new seed file
+
+Usage:
+ g:seed [options] [--] []
+
+Arguments:
+ model model name (optional)
+ name seed name
+
+Options:
+ -f, --factory Create a factory for seeder
+```
+
+## Next Steps
+
+- [d Commands](/aloe-cli/v/1.2.3/commands/d-commands)
+- [Custom commands](/aloe-cli/v/1.2.3/commands/custom)
+- [Commands IO](/aloe-cli/v/1.2.3/commands/io)
+- [Creating Libraries](/aloe-cli/v/1.2.3/libraries)
diff --git a/apps/docs/src/aloe-cli/v/1.2.3/commands/io.md b/apps/docs/src/aloe-cli/v/1.2.3/commands/io.md
new file mode 100755
index 0000000..d425992
--- /dev/null
+++ b/apps/docs/src/aloe-cli/v/1.2.3/commands/io.md
@@ -0,0 +1,328 @@
+# Aloe CLI: Command IO
+
+Command IO deals with how info flows in and out of your command. Aloe provides very simple ways to deal with both input and output from your commands.
+
+## Command Input
+
+Aloe provides a bunch of methods that allow users to make some form of input into the command. Just like the rest of Leaf, aloe prioritizes simplicity, and so, you can do almost anything you need in 1 line of code. All of these methods are already available once you extend the Aloe Command class and will be accessible on `$this`. Let's look at these methods.
+
+### input
+
+This method can return an input argument or the whole symfony console input object.
+
+```php
+public function handle()
+{
+ // returns the name argument
+ $name = $this->input("name");
+
+ // returns the whole whole input object
+ $input = $this->input();
+
+ // so you can do this
+ $name = $input->getArgument("name");
+}
+```
+
+### setArgument
+
+This method tells Aloe that your command is expecting an argument, it's typically used inside the `config` method.
+
+It typically follows the same convention as symfony console's `addArgument` except that instead of passing in `InputArgument::state`, you just pass in the state as a string.
+
+Instead of `InputArgument::REQUIRED`, you just pass in `"required"`, any case is supported.
+
+```php
+public function config()
+{
+ $this->setArgument("name", "required");
+}
+```
+
+### argument
+
+This method returns the value for a given argument passed into the command.
+
+```php
+$name = $this->argument("name");
+```
+
+### arguments
+
+This method returns all the given arguments merged with the default values.
+
+```php
+$name = $this->arguments("name");
+```
+
+### setOption
+
+This method tells Aloe that your command is expecting an inoput option, it's typically used inside the `config` method.
+
+It typically follows the same convention as symfony console's `addOption` except that instead of passing in `InputOption::state`, you just pass in the state as a string.
+
+Instead of `InputOption::VALUE_REQUIRED`, you just pass in `"required"`, any case is supported.
+
+```php
+public function config()
+{
+ $this->setOption("name", "n", "required");
+}
+```
+
+### option
+
+This method returns the value for a given option passed into the command.
+
+```php
+$name = $this->option("name");
+```
+
+### options
+
+This method returns all the given options merged with the default values.
+
+```php
+$name = $this->options("name");
+```
+
+### ask
+
+This method displays some output and returns a value from the expected input, in short, it asks a question and returns the users answer.
+
+```php
+// default value yes
+$shouldDelete = $this->ask("Delete file?", "yes");
+
+if ($shouldDelete === "yes") {
+ // delete file
+}
+```
+
+### askRaw
+
+This method displays some output and returns a value from the expected input as is, in short, it asks a question and returns the exact unformatted answer.
+
+```php
+// default value yes
+$shouldDelete = $this->ask("Delete file?", "yes");
+
+if ($shouldDelete === "yes") {
+ // delete file
+}
+```
+
+### autoComplete
+
+Ask a question but provide auto completion for possible answers.
+
+```php
+// possible answers are an array of
+// auto complete values
+$answers = [
+ "answer 1",
+ "answer 2"
+];
+$answer = $this->autoComplete($question, $answers, $default);
+
+// example
+$job = $this->autoComplete("what's your job?", [
+ "lawyer",
+ "doctor"
+]);
+```
+
+### choice
+
+Ask a question where 1 answer must be selected from a bunch of possible answers.
+
+```php
+$answers = [
+ "answer 1",
+ "answer 2"
+];
+$answer = $this->choice($question, $answers, $errorMessage, $default);
+
+// example
+$answer = $this->choice("What fruit do you like?", [
+ "fruit 1",
+ "fruit 2"
+]);
+```
+
+### multiChoice
+
+Ask a question where multiple answers can be selected from a bunch of possible answers.
+
+```php
+$answers = [
+ "answer 1",
+ "answer 2"
+];
+$answer = $this->multiChoice($question, $answers, $errorMessage, $default);
+
+// example
+$answer = $this->multiChoice("What fruit do you like?", [
+ "fruit 1",
+ "fruit 2"
+]);
+```
+
+### secret
+
+This method prompts a user for input, but hides the keystrokes
+
+```php
+$password = $this->secret("Confirm your password");
+```
+
+### confirm
+
+Prompts the user for confirmation (y/n)
+
+```php
+if ($this->confirm("Send cash?")) {
+ // send money
+} else {
+ // cancel
+}
+```
+
+## Command Output
+
+Just like input methods, there are a bunch of output methods and helpers that help you output information from your command. Let's look at these methods.
+
+### output
+
+This method can output some info or return the whole symfony console output object.
+
+```php
+public function handle()
+{
+ // outputs some data
+ $this->output("This is output");
+
+ // returns the whole whole output object
+ $output = $this->output();
+
+ // so you can do this
+ $name = $output->writeln("This is output");
+}
+```
+
+### write
+
+Writes a message to the output.
+
+```php
+$this->write("This is some output");
+```
+
+### writeln
+
+Writes a message to the output and adds a newline at the end.
+
+```php
+$this->writeln("This is some output");
+```
+
+### comment
+
+Writes a comment styled message to the output and adds a newline at the end.
+
+```php
+$this->comment("This is some output");
+```
+
+### info
+
+Writes a info styled message to the output and adds a newline at the end.
+
+```php
+$this->info("This is some output");
+```
+
+### error
+
+Writes a error styled message to the output and adds a newline at the end.
+
+```php
+$this->error("This is some output");
+```
+
+### question
+
+Writes a question styled message to the output and adds a newline at the end.
+
+```php
+$this->question("This is some output");
+```
+
+### link
+
+Writes a link to the output and adds a newline at the end.
+
+```php
+$this->link("https://mychi.netlify.app", "Mychi");
+```
+
+## IO Helper Methods
+
+These are helper methods that can be called inside your commands anywhere. These methods typically give option to your input or output.
+
+### asQuestion
+
+asQuestion does pretty much the same thing as `question` above, except that `asQuestion` simply returns the `question` styles for text.
+
+```php
+$this->writeln("Question: " . asQuestion(" ...?"));
+```
+
+This allows you to use the question output style for only a section of your output.
+
+### asComment
+
+asComment does pretty much the same thing as `comment` above, except that `asComment` simply returns the `comment` styles for text.
+
+```php
+$this->writeln("Comment: " . asComment(" ..."));
+```
+
+This allows you to use the comment output style for only a section of your output.
+
+### asInfo
+
+asInfo does pretty much the same thing as `info` above, except that `asInfo` simply returns the `info` styles for text.
+
+```php
+$this->writeln("Info: " . asInfo(" ..."));
+```
+
+This allows you to use the info output style for only a section of your output.
+
+### asError
+
+asError does pretty much the same thing as `error` above, except that `asError` simply returns the `error` styles for text.
+
+```php
+$this->writeln("Error: " . asError(" ..."));
+```
+
+This allows you to use the error output style for only a section of your output.
+
+### asLink
+
+asLink does pretty much the same thing as `link` above, except that `asLink` simply returns the `link` option for text.
+
+```php
+$this->writeln("link: " . asLink("https://...", "display text"));
+```
+
+This allows you to display a link on only part of your output.
+
+## Next Steps
+
+- [G Commands](/aloe-cli/v/1.2.3/commands/g-commands)
+- [Custom commands](/aloe-cli/v/1.2.3/commands/custom)
+- [Commands IO](/aloe-cli/v/1.2.3/commands/io)
+- [DB commands](/aloe-cli/v/1.2.3/commands/db-commands)
diff --git a/apps/docs/src/aloe-cli/v/1.2.3/commands/misc-commands.md b/apps/docs/src/aloe-cli/v/1.2.3/commands/misc-commands.md
new file mode 100755
index 0000000..581ba6c
--- /dev/null
+++ b/apps/docs/src/aloe-cli/v/1.2.3/commands/misc-commands.md
@@ -0,0 +1,50 @@
+# Aloe CLI: Misc Commands
+
+## Serve command
+
+This command allows you to run your Leaf app using PHP's built in server.
+
+```bash
+Description:
+ Start the leaf development server
+
+Usage:
+ serve [options] [--] []
+
+Arguments:
+ path Path to your app (in case you changed it)
+
+Options:
+ -p, --port[=PORT] Port to run Leaf app on [default: 5500]
+```
+
+## Scaffolding
+
+Scaffolding is a feature that allows you to generate big blocks of code and full features without writing a single line of code. For now, auth scaffolding is the only type of scffolding available in Aloe CLI, however, it comes ready built for both APIs and web apps.
+
+### auth scaffolding
+
+This will generate views, controllers, routes and models required for authentication and provide your app with working authentication in only one command.
+
+```bash
+$ php leaf scaffold:auth
+
+Description:
+ Scaffold basic app authentication
+
+Usage:
+ scaffold:auth [options]
+
+Options:
+ -s, --session Use session/session + JWT instead of just JWT
+ -a, --api Use JWT for authentication
+```
+
+**Note that aloe will automatically use the session version on Leaf MVC and the api version on Leaf API.**
+
+## Next Steps
+
+- [DB Commands](/aloe-cli/v/1.2.3/commands/db-commands)
+- [Custom commands](/aloe-cli/v/1.2.3/commands/custom)
+- [Commands IO](/aloe-cli/v/1.2.3/commands/io)
+- [Creating Libraries](/aloe-cli/v/1.2.3/libraries)
diff --git a/apps/docs/src/aloe-cli/v/1.2.3/getting-started/index.md b/apps/docs/src/aloe-cli/v/1.2.3/getting-started/index.md
new file mode 100755
index 0000000..bcd7fe8
--- /dev/null
+++ b/apps/docs/src/aloe-cli/v/1.2.3/getting-started/index.md
@@ -0,0 +1,171 @@
+# Aloe CLI: Getting Started
+
+::: tip Note
+If you're using Leaf MVC or Leaf API, you can skip this page.
+:::
+
+Aloe CLI is a smart CLI that takes care of a lot of a whole lot of time consuming tasks during your development, allowing you to focus only on important stuff.
+
+**Aloe is based on the symfony console, with that, all symfony commands are also valid aloe commands and can be called through the aloe cli.**
+
+*Currently, Aloe is only fully supported by Leaf MVC and Leaf API, as some commands may not work on other setups.*
+
+For better support if you're not using Leaf MVC or Leaf API, you'll need to install Leaf MVC Core which contains all of Leaf's MVC utilities including a special autoloader and configuration...
+
+```bash
+leaf install mvc-core
+```
+
+Or with composer:
+
+```bash
+composer require leafs/mvc-core
+```
+
+## Installation
+
+After installing MVC Core, you can install Aloe with leaf CLI.
+
+```bash
+leaf install aloe
+```
+
+Or with composer:
+
+```bash
+composer require leafs/aloe
+```
+
+## Setup
+
+After installing aloe, you need to set it up so you can run commands like `php leaf ...`. To do this, simply create a file with the name you want to run in your console. eg: To run commands using `php console ...`, you'll need to create a file named `console`. With Leaf API and Leaf MVC, this file is named `leaf` which is the reason you run commmands with `php leaf ...`. We'll name this file `poison` which means we'll run commands like `php poison serve`
+
+In the `poison` file, we need to do a couple of things:
+
+- Bring in composer's autoloader
+- Bring in a custom autoloader to dynamically require your migrations, seeds and other files when they're directly initialized in the CLI.
+- Bring in our env variables
+- Finally, load up aloe and it's commands
+
+The setup used in Leaf MVC looks like this:
+
+```php
+#!/usr/bin/env php
+ 'app/controllers',
+ 'modelsPath' => 'app/models',
+ 'migrationsPath' => 'app/database/migrations',
+ 'seedsPath' => 'app/database/seeds',
+ 'factoriesPath' => 'app/database/factories',
+ 'helpersPath' => 'app/helpers',
+ 'viewsPath' => 'app/views',
+ 'configPath' => 'config',
+ 'storagePath' => 'storage',
+ 'commandsPath' => 'app/console',
+ 'routesPath' => 'app/routes',
+ 'libPath' => 'lib',
+ 'publicPath' => 'public',
+ 'databaseStoragePath' => 'storage/app/db'
+]);
+```
+
+This config tells aloe where to find everything it needs to get going. Under the hood, any work needed will be done for you. From here, it's simply a matter of adding our env, initializing our database and aloe.
+
+```php
+/*
+|--------------------------------------------------------------------------
+| Bring in (env)
+|--------------------------------------------------------------------------
+|
+| Quickly use our environment variables
+|
+*/
+try {
+ \Dotenv\Dotenv::createUnsafeImmutable(__DIR__)->load();
+} catch (\Throwable $th) {
+ trigger_error($th);
+}
+
+/*
+|--------------------------------------------------------------------------
+| Additional Leaf Database Config
+|--------------------------------------------------------------------------
+|
+| Load leaf database configuration
+|
+*/
+Leaf\Database::config(DatabaseConfig());
+Leaf\Database::connect();
+
+/*
+|--------------------------------------------------------------------------
+| Initialise Leaf CMD
+|--------------------------------------------------------------------------
+|
+| Initialise aloe CLI
+|
+*/
+$console = new \Aloe\Console("Leaf MVC", "v3.0");
+
+/*
+|--------------------------------------------------------------------------
+| Add commands
+|--------------------------------------------------------------------------
+|
+| Add custom commands
+|
+*/
+$console->register(\App\Console\ExampleCommand::class);
+
+/*
+|--------------------------------------------------------------------------
+| Run The console Application
+|--------------------------------------------------------------------------
+|
+| Transport water and dissolved substances to the rest of Leaf😂
+|
+*/
+$console->run();
+```
+
+Leaf API's config includes a little twist which lets Aloe run in API first mode. Which means aloe will run the API centered version of commands if available.
+
+```php
+\Aloe\Command\Config::$env = "API";
+```
+
+So depending on your use case, the API commands may be of more use.
+
+## Next Steps
+
+- [DB Commands](/aloe-cli/v/1.2.3/commands/db-commands)
+- [Custom commands](/aloe-cli/v/1.2.3/commands/custom)
+- [Commands IO](/aloe-cli/v/1.2.3/commands/io)
+- [Creating Libraries](/aloe-cli/v/1.2.3/libraries)
diff --git a/apps/docs/src/aloe-cli/v/1.2.3/index.md b/apps/docs/src/aloe-cli/v/1.2.3/index.md
new file mode 100644
index 0000000..b62439b
--- /dev/null
+++ b/apps/docs/src/aloe-cli/v/1.2.3/index.md
@@ -0,0 +1,113 @@
+# Sunset Aloe (BETA)
+
+v1.1 beta is the secomd release of Aloe CLI which introduces full support for Leaf MVC, new features, better support for custom libraries and so much more. Sunset Aloe also introduces integrations with Leaf Auth, creating a scaffold option to make authentication available in your app with only 1 command.
+
+## What's new
+
+### Better Leaf MVC support
+
+Sunset Aloe is the first version of Aloe integrated out of the box with Leaf MVC. As such, it ships a tight integration with Leaf MVC and the already made integrations that come with Leaf API. So now, your Leaf MVC CLI is powered by aloe.
+
+### Auth Scaffolding
+
+Sunset also includes auth scaffolding which simply allows you to add basic session based auth (login, signup, update profile and guards) to your app by simply running one command.
+
+```bash
+php leaf scaffold:auth
+```
+
+### Updated Stubs
+
+These are basic templates generated when you run commands like `g:controller`. These templates have been updated to keep you up to date with both internal and external updates from Leaf API and Leaf MVC.
+
+### Aloe Installer Class
+
+Another major update to Aloe is the inclusion of Aloe Installer which basically takes the stress out of making libraries which need to install files/routes in the working directory.
+
+```php
+use Aloe\Installer;
+
+Installer::magicCopy($folderToInstall);
+Installer::installRoutes("$folderToInstall/routefiles/");
+```
+
+**Installer currently only supports Leaf MVC and Leaf API.**
+
+### Updated Packages
+
+All dependencies of Aloe have been updated. This includes security patches and a bunch of updates to keep aloe up to date. Also, the core library behind aloe, symfony console has been updated as well, however, we do our best to maintain Aloe CLI's syntax, structure and config, so despite all the updates and external changes, the Aloe you know never changes.
+
+### Protected command methods
+
+To match the symfony console, Aloe also uses protected command methods in this version.
+
+```php
+protected function config()
+{
+ $this
+ ->setArgument("argument", "optional", "argument description")
+ ->setOption("option", "o", "required", "option description");
+}
+
+protected function handle()
+{
+ $this->comment(
+ "example command's output {$this->argument('argument')} {$this->option('option')}"
+ );
+}
+```
+
+## Aloe List
+
+```bash
+Leaf MVC v2.0
+
+Usage:
+ command [options] [arguments]
+
+Options:
+ -h, --help Display help for the given command. When no command is given display help for the list command
+ -q, --quiet Do not output any message
+ -V, --version Display this application version
+ --ansi Force ANSI output
+ --no-ansi Disable ANSI output
+ -n, --no-interaction Do not ask any interactive question
+ -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
+
+Available commands:
+ example example command's description
+ help Displays help for a command
+ interact Interact with your application
+ list Lists commands
+ serve Start the leaf development server
+ aloe
+ aloe:config Install aloe config
+ app
+ app:down Place app in maintenance mode
+ app:up Remove app from maintenance mode
+ d
+ d:command Delete a console command
+ d:controller Delete a controller
+ d:factory Delete a model factory
+ d:migration Delete a migration
+ d:model Delete a model
+ d:seed Delete a model seeder
+ db
+ db:install Create new database from .env variables
+ db:migrate Run the database migrations
+ db:rollback Rollback all database migrations
+ db:seed Seed the database with records
+ env
+ env:generate Generate .env file
+ g
+ g:command Create a new console command
+ g:controller Create a new controller class
+ g:factory Create a new model factory
+ g:helper Create a new helper class
+ g:migration Create a new migration file
+ g:model Create a new model class
+ g:seed Create a new seed file
+ g:template Create a new view file
+ scaffold
+ scaffold:auth Scaffold basic app authentication
+```
diff --git a/apps/docs/src/aloe-cli/v/1.2.3/installer.md b/apps/docs/src/aloe-cli/v/1.2.3/installer.md
new file mode 100644
index 0000000..d2147d2
--- /dev/null
+++ b/apps/docs/src/aloe-cli/v/1.2.3/installer.md
@@ -0,0 +1,56 @@
+# Aloe Installer
+
+Aloe installer allows you to quickly install files and routes from your library into the working directory. Aloe installer currently only suports Leaf MVC and Leaf API.
+
+To get started with Aloe installer, just import the package into your app or directly use the method you want to use directly.
+
+```php
+use Aloe\Installer;
+
+// or
+
+Aloe\Installer::method();
+```
+
+## Magic Copy
+
+This method allows you to auto-magically copy all files and folders from a specified folder into Leaf workspace.
+
+```php
+Aloe\Installer::magicCopy("package/to/install");
+```
+
+Consider the following directory structure:
+
+```bash
+C:.
+└───Auth
+ ├───Controllers
+ ├───Routes
+ └───Views
+```
+
+To copy our controllers, routes and views, we simply need to point `magicCopy` to the auth directory.
+
+```php
+Aloe\Installer::magicCopy("package/Auth");
+```
+
+This will copy the sub directories in Auth to the `App` folder in the working directory.
+
+## Install Routes
+
+Similarly, you can also automatically install routes from your package routes in your app after you've copied them into the working routes directory.
+
+Installing routes involves referencing routes files in the main route file.
+
+```php
+Aloe\Installer::installRoutes("package/Auth/Routes");
+```
+
+## Next Steps
+
+- [g Commands](/aloe-cli/v/1.2.3/commands/g-commands)
+- [Custom commands](/aloe-cli/v/1.2.3/commands/custom)
+- [Commands IO](/aloe-cli/v/1.2.3/commands/io)
+- [db commands](/aloe-cli/v/1.2.3/commands/db-commands)
diff --git a/apps/docs/src/aloe-cli/v/1.2.3/libraries.md b/apps/docs/src/aloe-cli/v/1.2.3/libraries.md
new file mode 100755
index 0000000..bb11a86
--- /dev/null
+++ b/apps/docs/src/aloe-cli/v/1.2.3/libraries.md
@@ -0,0 +1,22 @@
+# Aloe CLI: Libraries
+
+This section talks about using and creating libraries for Aloe CLI, as well as some best practices in terms of custom libraries.
+
+## Plugging in libraries
+
+As seen in [registering commands](/aloe-cli/v/1.2.3/commands/custom?id=registering-commands), Aloe provides a neat `register` method which allows you to extend aloe's command range. Aloe libraries usually take advantage of this to return an array instead of manually returning commands one by one.
+
+```php
+$console->register(\Aloe\UI::commands());
+```
+
+## Creating your own libraries
+
+Aloe libraries simply serve a bunch of commands to the Aloe CLI. ALoe CLI has full support for Symfony console commands, so all symfony commands can also be run through Aloe CLI. After defining all your commands, you can simply return all their classes as an array to be registered.
+
+## Next Steps
+
+- [Installer](/aloe-cli/v/1.2.3/installer)
+- [Custom commands](/aloe-cli/v/1.2.3/commands/custom)
+- [Commands IO](/aloe-cli/v/1.2.3/commands/io)
+- [db commands](/aloe-cli/v/1.2.3/commands/db-commands)
diff --git a/apps/docs/src/changes.md b/apps/docs/src/changes.md
new file mode 100644
index 0000000..e5f49ed
--- /dev/null
+++ b/apps/docs/src/changes.md
@@ -0,0 +1,929 @@
+
+# Changelog
+
+## v3.4.0 - 🥀 Hello Darkness - 19 May 2023
+
+### Added
+
+- Added support for library scripts
+- Added Eien config
+- Added support for named middleware
+
+### Changed
+
+- Moved ALL error handling responsibilities to `\Leaf\Exception`
+
+### Removed
+
+- Moved module initializers to scripts
+
+## v3.3.1 - 🌺 Midnight Wine '1' - 11 Mar 2023
+
+### Fixed
+
+- Patched up `app` config resetting after new values are added
+- Improved performance of application config setting on first instance
+
+## v3.3.0 - 🌺 Midnight Wine - 27 Feb 2023
+
+### Added
+
+- Added support for Eien websockets
+
+### Fixed
+
+- Hard set status on CSRF error
+- Fixed non-static methods error
+
+### Changed
+
+### Removed
+
+- Removed obsolete code
+
+
+
+## v3.2.2-beta - 🌺 Viola “Black Delight” [Eien Compat Build 2] - 25 Dec 2022
+
+### Added
+
+- Added support for Eien websockets
+
+### Fixed
+
+- Fixed non-static methods error
+- Fixed issue with CSRF error
+
+### Removed
+
+- Removed obsolete code
+
+## v3.2.0 - 🌺 Viola “Black Delight” - 3 Oct 2022
+
+### Added
+
+- Made leaf config reactive
+
+### Fixed
+
+- Fixed issue [`debug = false` not working as expected](https://github.com/leafsphp/leaf/issues/152)
+- Fixed issue with setRequestClass
+- Updated references to renamed methods
+
+### Changed
+
+- Updated leaf modules
+- Removed version constraints from modules
+- Updated tests
+
+### Removed
+
+- Removed obsolete methods
+
+## v3.1.1 - 🌷 Cantor Black '1' - 10 Sep 2022
+
+### Fixed
+
+- [Fix error: Cannot use Container as array](https://github.com/leafsphp/leaf/commit/9d51e6bfc8a246d688ad8848f17cdf7eddaaf8e5)
+
+### Changed
+
+- Updated Leaf Http
+
+## v3.1.0 - 🌷 Cantor Black - 11 Aug 2022
+
+### Added
+
+- Allow custom response object
+- Allow custom request object
+
+## v3.0 - 👸🏼 Queen Of The Night - 15 Apr, 2022
+
+### Added
+
+- Added support for global functions
+- Added unit tests
+- Added pestphp
+- Added more support for configuring with env variables.
+- Added `script` method on `app` to handle modes.
+- Added automatic loading for major modules.
+- Added CSRF handler.
+- Added autoloading for the CSRF module
+- Added CORS handler for `leafs/cors`
+- Updated error pages
+- Added `.env` precedence over local config
+- Added support for recursive config
+- Added support for nested config
+- Added support for custom setting groups
+- Switched to leaf exceptions
+
+### Fixed
+
+- Fixed PHP 7.3 unsupported types
+- Created a standard for functional mode
+- Revamped error pages
+- Fixed potential type errors
+- Fixed malformed JWT error on auth module
+- Added single state for container settings and leaf config
+- Fixed breaks in PHP 8+
+
+### Changed
+
+- You no longer need to initialize Leaf.
+- Moved `Leaf\Http` namespace to HTTP module
+- Moved base controller, model, database and to `mvc-core` module
+- Disabled logging by default
+- Moved bareui, fs, auth, form, cookies, session, db and date into modules
+- Moved `Leaf\Router` into installable modules.
+- Changed leaf router `add` to `use`
+- Tied leaf router directly to `Leaf\App`
+- Upgraded all dependencies
+- Added strict types
+
+### Removed
+
+- Removed unused dependencies
+- Removed experimental features
+- Removed unused initializers
+- Removed `evadeCors` method on leaf
+- Removed `Leaf\Router::getRequestMethod`
+- Removed unnecessary code to improve performance
+- Removed app instance on middleware
+- Removed unused helper functions
+- Removed leftover files from clean up
+
+## v3.0 (Release Candidate 2) - 👸🏼 Queen Of The Night (RC 2) - 27 Jan, 2022
+
+### Added
+
+- Added support for global functions
+- Added unit tests
+- Added pestphp
+- Added more support for configuring with env variables.
+- Added `script` method on app to handle modes.
+- Added automatic loading for major modules.
+- Added CSRF handler.
+- Added autoloading for CSRF module
+- Added CORS handler for `leafs/cors`
+- Updated error pages
+- Added `.env` precedence over local config
+- Added support for recursive config
+- Added support for nested config
+- Added support for custom setting groups
+- Switched to leaf exceptions
+
+### Fixed
+
+- Fixed PHP 7.3 unsupported types
+- Created a standard for functional mode
+- Revamped error pages
+- Fixed potential type errors
+- Fixed malformed JWT error on auth module
+- Added single state for container settings and leaf config
+- Fixed breaks in PHP 8+
+
+### Changed
+
+- You no longer need to initialize Leaf.
+- Moved `Leaf\Http` namespace to Http module
+- Moved base controller, model, database and to mvc-core module
+- Disabled logging by default
+- Moved bareui, fs, auth, form, cookies, session, db and date into modules
+- Moved `Leaf\Router` into installable module.
+- Changed leaf router `add` to `use`
+- Tied leaf router directly to `Leaf\App`
+- Upgraded all dependencies
+- Added strict types
+
+### Removed
+
+- Removed unused dependencies
+- Removed experimental features
+- Removed unused initializers
+- Removed `evadeCors` method on leaf
+- Removed `Leaf\Router::getRequestMethod`
+- Removed unnecessary code to improve performance
+- Removed app instance on middleware
+- Removed unused helper functions
+- Removed left over files from clean up
+
+## v3.0 (Beta) - 27 November
+
+### Added
+
+- Added support for global functions
+- Added more support for configuring with env variables.
+- Added `script` method on app to handle modes.
+- Added automatic loading for major modules.
+- Added CSRF handler.
+- Added autoloading for CSRF module
+- Added CORS handler for `leafs/cors`
+- Updated error pages
+- Added `.env` precedence over local config
+
+### Fixed
+
+- Fixed PHP 7.3 unsupported types
+- Created a standard for functional mode
+- Revamped error pages
+- Fixed potential type errors
+
+### Changed
+
+- You no longer need to initialize Leaf.
+- Moved `Leaf\Http` namespace to Http module
+- Moved base controller, model, database and to mvc-core module
+- Disabled logging by default
+- Moved bareui, fs, auth, form, cookies, session, db and date into modules
+- Moved `Leaf\Router` into installable module.
+- Changed leaf router `add` to `use`
+- Tied leaf router directly to `Leaf\App`
+- Upgraded all dependencies
+
+### Removed
+
+- Removed unused dependencies
+- Removed experimental features
+- Removed unused initializers
+- Removed `evadeCors` method on leaf
+- Removed `Leaf\Router::getRequestMethod`
+- Removed unnecessary code to improve performance
+- Removed app instance on middleware
+- Removed unused helper functions
+
+## v3.0 (Alpha) - 👸🏼 Queen Of The Night
+
+### Added
+
+- Added support for global functions
+- Added more support for configuring with env variables.
+- Added `script` method on app to handle modes.
+- Added automatic loading for major modules.
+- Added CSRF handler.
+- Added autoloading for CSRF module
+- Added CORS handler for `leafs/cors`
+- Updated error pages
+- Added `.env` precedence over local config
+
+### Fixed
+
+- Fixed PHP 7.3 unsupported types
+- Created a standard for functional mode
+- Revamped error pages
+- Fixed potential type errors
+
+### Changed
+
+- You no longer need to initialize Leaf.
+- Moved `Leaf\Http` namespace to Http module
+- Moved base controller, model, database and to mvc-core module
+- Disabled logging by default
+- Moved bareui, fs, auth, form, cookies, session, db and date into modules
+- Moved `Leaf\Router` into installable module.
+- Changed leaf router `add` to `use`
+- Tied leaf router directly to `Leaf\App`
+- Upgraded all dependencies
+
+### Removed
+
+- Removed unused dependencies
+- Removed experimental features
+- Removed unused initializers
+- Removed `evadeCors` method on leaf
+- Removed `Leaf\Router::getRequestMethod`
+- Removed unnecessary code to improve performance
+- Removed app instance on middleware
+- Removed unused helper functions
+
+## v2.6.0 - ⚰️ The Goodbye Flower - 20th September, 2021
+
+### Added
+
+- Added UUID support to Leaf Auth
+- Added support for custom id keys in Leaf Auth
+
+### Fixed
+
+- Fixed Request::getUrl
+- Fixed issue [#53](https://github.com/leafsphp/leaf/issues/53)
+- Fixed Database
+
+### Removed
+
+- Removed Leaf blade component
+
+## v2.5.1 - 💠 Lilac - 30th May, 2021
+
+### Fixed
+
+- Fixed PHP 7.3 unsupported types
+- Fixed server base path on router
+- Fixed bare UI config method
+- Fixed faker namespace
+
+### Changed
+
+- Removed BETA flag from Leaf password helper
+
+### Removed
+
+- Removed Leaf blade component
+
+## v2.5.0 - 💠 Gladiolus - 27th April, 2021
+
+### Added
+
+- Leaf debug now controls error reporting (you don't want nasty errors showing in production)
+- Added `Request::try`
+- Added `app.down` config
+- Added Leaf app instance on `Config`
+- Added grouped namespaces to router
+- Added single route namespaces
+- Added named routes to router
+- Added router `push` for switching between pages
+- Added more customizations on `Leaf\Database`
+- Added simple flash messaging with `Leaf\Flash`
+- Added `flash` method to session
+- Added HTTP caching on `Leaf\Http\Headers`
+
+### Fixed
+
+- Fixed inverted condition for showing default development/production error pages.
+- Fixed router hooks
+- Added proper types on `App` and `Router`
+- Added proper controller and missing method warnings
+- Fixed incorrect method labeling
+- Fixed HTTP caching issues
+- Fixed app logger and app log writer
+- Fixed app break after non-existent middlware call
+
+### Changed
+
+- Switched debugging controls from `mode` to `debug`
+- Default 404 page now automatically loaded
+- Router middleware `App` instance now automatically loaded
+- Added null response for unset session variables
+- Leaf error handler now loads on `App` init
+- Default error 500 handler now automatically loaded
+- Updated leaf container
+
+### Removed
+
+- Removed unnecessary code from `App`
+- Removed app name
+- Removed `view` method on app and router
+- Removed previous hook support on app
+- Removed unused router config
+- Removed Leaf environment class
+- Removed unused default middleware
+- `Leaf\Blade` no longer comes with Leaf by default.
+- Removed `status` and `contentType` on Leaf\App
+
+## v2.5.0-beta - 💠 Gladiolus (BETA) - 16th April, 2021
+
+### Added
+
+- Added `app.down` config
+- Added Leaf app instance on `Config`
+- Added grouped namespaces to router
+- Added named routes to router
+- Added router group prefixes
+- Added router `push` for switching between pages
+- Added more customizations on `Leaf\Database`
+- Added simple flash messaging with `Leaf\Flash`
+- Added `flash` method to session
+- Added HTTP caching on `Leaf\Http\Headers`
+
+### Fixed
+
+- Fixed router hooks
+- Added proper types on `App` and `Router`
+- Added proper controller and missing method warnings
+- Fixed incorrect method labeling
+- Fixed HTTP caching issues
+- Fixed app logger and app log writer
+- Fixed app break after non-existent middlware call
+
+### Changed
+
+- Default 404 page now automatically loaded
+- Router middleware `App` instance now automatically loaded
+- Added null response for unset session variables
+- Leaf error handler now loads on `App` init
+- Default error 500 handler now automatically loaded
+- Updated leaf container
+
+### Removed
+
+- Removed unnecessary code from `App`
+- Removed app name
+- Removed `view` method on app and router
+- Removed previous hook support on app
+- Removed unused router config
+- Removed Leaf environment class
+- Removed unused default middleware
+- `Leaf\Blade` no longer comes with Leaf by default.
+- Removed `status` and `contentType` on Leaf\App
+
+## v2.4.4 - 🎋 Common Reed - 23rd March 2021
+
+### Added
+
+- Added `Leaf\Config` for easier configuration
+- Added new leaf config options
+- Added `Leaf\View`
+- Added support for multiple template engines concurrently
+- Added BareUI templating engine
+
+### Fixed
+
+- Internal code improvements on App
+
+### Changed
+
+- No renames, restructures, ...
+
+### Removed
+
+- No removals
+
+## v2.4.3 - 🎋 Giant Cane Grass - 26th February 2021
+
+### Added
+
+- Updated `Leaf\Db` and `Leaf\Auth` to throw dev errors to Leaf's error handler for better error reporting
+
+### Fixed
+
+- Organized methods in `Leaf\FS`
+
+### Changed
+
+- Made `Leaf\Http\Response` static
+- Made `Leaf\Http\Request` static
+
+### Removed
+
+- No removals
+
+## v2.4.2 - 🥬 Desert Wishbone-bush - 3rd February 2021
+
+This version of Leaf continues the goal of making Leaf features more flexible and increasing usability.
+
+### Added
+
+- Added option to turn off experimental method warnings
+
+- Added `Form::rule` which allows you to create your own rules for form validation.
+
+```php
+Form::rule("max", function ($field, $value, $params) {
+ if (strlen($value) > $params) {
+ Form::addError($field, "$field can't be more than $params characters");
+ return false;
+ }
+});
+```
+
+- Added internal `Leaf\Form` feature which allows you to pass parameters to validation rules.
+
+```php
+$validation = Form::validate([
+ // To pass a param to a rule, just use :
+ "username" => "max:3",
+]);
+```
+
+- Added `Form::addError` which allows you to add errors to be returned in `Form::errors()`
+
+```php
+Form::addError($field, "$field can't be more than $params characters");
+```
+
+- Added max and min rules by default
+
+```php
+$validation = Form::validate([
+ "username" => "max:1",
+ "password" => "min:81",
+]);
+```
+
+- Guards can be used even in API mode. This will alert you if you're not eligible to view a particular page.
+
+### Fixed
+
+- Updated dependencies with security patches
+
+- Fixed multiple validation break from v2.4.2 beta.
+
+### Changed
+
+- Made `Leaf\Form` methods static. They can now be called from anywhere within your Leaf app.
+
+### Removed
+
+- No removals
+
+## v2.4.2 [BETA] - 🥬 Desert Wishbone-bush (Beta) - 20th January 2021
+
+This release mainly focuses on security patches for all Leaf based libraries. It contains updated dependencies and internal code patches to make your apps even more secure.
+
+### Added
+
+- No additions
+
+### Fixed
+
+- Updated dependencies with security patches
+
+### Changed
+
+- Made `Leaf\Auth` methods static. They can now be called from anywhere within your Leaf app.
+
+### Removed
+
+- No removals
+
+## v2.4.1 - 🍁 Marvel-of-peru - 12th January 2020
+
+v2.4.1 continues the usability reforms from the previous versions. It also contains fixes for all bugs discovered in previous versions as well as new features.
+
+**Please update from v2.4.0 to v2.4.1 to fix any issues you encountered with the system. Any inconveniences are deeply regretted🙏.**
+
+### Added
+
+- Added support for session based authenticatication instead of just JWT
+- Added `Route::view`
+
+### Fixed
+
+- Fixed all known bugs from previous versions
+
+### Changed
+
+- Separated Router module from app module
+- Made all `Leaf\Http\Session` methods static
+
+### Removed
+
+- Removed app down feature
+
+## v2.4.0 - Christmas Tree🎄 - 18th December 2020 ***DELETED***
+
+**Please update from v2.4.0 to v2.4.1 to fix any issues you encountered with this version. Any inconveniences are deeply regretted🙏. This release has officially been deleted.**
+
+Christmas tree follows up on the previous beta release, fixes up all bugs found during the beta testing phase and packs in newer extensions that make Leaf even more usable.
+
+### Added
+
+- Added base factory class for Leaf MVC, Leaf API and Skeleton
+- Added new auth setting options
+
+### Fixed
+
+- Fixed `Leaf\Db` callstack not clearing
+- Fixed `Auth::update` db errors
+- Fixed `Auth::update` including current user in uniques check
+- Fixed password verify method params
+
+### Changed
+
+- Switched default password encryption to `PASSWORD_DEFAULT` (bcrypt by default)
+- Auth now relies on Leaf password helper for everything password related
+- Standardized all `where` type methods on `Leaf\Db`
+- Seperated password encoding and password verifying settings in `Leaf\Auth`
+- Switched password helper methods to camelCase
+- Switched password `salt` with `spice` to add additional security to passwords
+
+### Removed
+
+- Removed unnecessary methods from password helper
+
+## v2.4.0 - BETA - 30th November, 2020
+
+Unlike previous versions, this version of Leaf is focusing on improving the use of existing features, rather than just pumping new magic into Leaf. It has a lot of bug fixes, standardization of method names and overall upgrades.
+
+### Added
+
+- Added `App::evadeCors`
+- Added `App::routes` to preview all routes
+- Added `Db::first()`
+- Leaf DB can now detect query type even when `query`
+- Added `orWhere`, `whereLike`, `orWhereLike` `like`, `orLike`, `orderBy`, `all` `limit` and LIKE helpers to Leaf Db
+- Added new format to `Date::now`
+- Added `Auth::update`
+- Added custom token lifetime support on `Auth`
+
+### Fixed
+
+- Fixed login bug with `Auth::currentUser`
+- Fixed Leaf DB same value bug
+- Minor fixes on `Auth::login` and `Auth::register`
+
+### Changed
+
+- Switched methods to camel case
+- Renamed `Auth::useToken` to `Auth::id`
+- Renamed `Auth::currentUser` to `Auth::user`
+- Made `Helpers\JWT` and `Helpers\Authentication` methods static
+
+### Removed
+
+- Removed `Form::isEmpty` and `Form::isNull`
+- Removed deprecated methods from `Response`
+- Removed deprecated methods from `Date`
+
+## v2.3.0 - Lucky Charm🍀 - Aug 15, 2020
+
+### Added
+
+- Added Leaf\Auth::useToken
+- Added Leaf\FS::upload_file
+- Added manual init to Leaf\Session
+- Added option for status code messages
+- Added callable utils
+- Added session encoding/decoding
+- Leaf\Http\Request now catches files passed into request
+- Added Leaf\Http\Request::typeIs
+- Leaf\Http\Request::get can now return multiple request data at once
+- Added Leaf\Http\Request::files
+- New Leaf\Http\Headers package
+- More untracked additions
+
+### Fixed
+
+- fixed Leaf\Http\Headers
+- Fixed response http status codes bug
+- Fixed header integration with response
+- Fixed header reliance on Set
+- Fixed throwErr code error
+- Fixed Leaf\Session package
+- Fixed response redirect
+- Fixed Leaf\Http\Request::body bugs
+- Sessions return false instead of throwing errors (Fix for web apps)
+- FS returns false instead of throwing errors
+- Fixed up Leaf\Http\Request::params
+- Fixed up Leaf\Http\Request::hasHeader
+- Fixed up header related methods on Leaf\Http\Request
+- Fixed bugs on Leaf\Environment
+- More untracked fixes
+
+### Changed
+
+- Switched Leaf\Session to native PHP sessions
+- Switched session package in Leaf\App
+- Changed controller file uploads to Leaf\FS
+- Leaf\Date methods can now be called static-ly
+- Switched Leaf\Date methods to camel case, but- with backward compatability for snake_case
+- Made all Leaf\FS methods static
+
+### Removed
+
+- Removed old session code
+- Removed setEncryptedCookie and getEncryptedCookie- on Leaf\App
+- Slashed unnecessary code from Leaf\Http\Request
+- Slashed unnecessary code from Leaf\Http\Session
+- Slashed unnecessary code from Leaf\Http\Cookie
+- Slashed unnecessary code from Leaf\Http\Response
+- Removed all method type tests from Leaf\Http\Request
+
+## v2.2.0 - Angel's Trumpet - Jul 7, 2020
+
+### Added
+
+- Added `Leaf\Auth::currentUser`
+- Added new cookies package relying on PHP's setcookie
+
+### Fixed
+
+- fixed hidden fields on Leaf\Auth::login
+- Fixed multiple-request type data on get and body at Leaf\Http\Request
+
+### Changed
+
+- Switched cookies package in Leaf\Http\Response
+- Switched cookies package in Leaf\App
+
+### Removed
+
+- Removed old cookies package and all it's methods
+- Removed setEncryptedCookie and getEncryptedCookie on Leaf\App
+- Slashed unnecessary code from Leaf\Http\Request
+
+## v2.1.0 - Elderberry - 19th June, 2020
+
+### Added
+
+- Added `Leaf\Auth::auto_connect`
+- Added default bypass for CORS errors
+- Added `Mysqli::auto_connect`
+- Added optional `db_type` option to `Leaf\Db\PDO` connection
+- Added `PDO::auto_connect`
+- Added deprecation warning for `Leaf\Db\PDO`
+
+### Fixed
+
+### Changed
+
+### Removed
+
+- Removed Leaf\Wynter
+
+## v2.1.0 - alpha - 24th May, 2020
+
+### Added
+
+- Added Route::resource
+- Added Session::retrieve
+
+### Fixed
+
+### Changed
+
+- Seperated Leaf Veins from Leaf Package
+- Renamed Session::getBody to Session::body
+
+### Removed
+
+- Removed Leaf\View
+
+## v2.0 - official - 21st April, 2020
+
+### Added
+
+- Added Leaf Mail
+- Added Date::days_ago
+- Added Date::months_ago
+- Added Date::years_ago
+- Added Date::day
+- Added Date::month
+- Added Date::year
+- Added Auth::setSecretKey
+- Added Auth::getSecretKey
+- Added Auth::validate
+- Added Leaf JS Scripts [BETA]
+- Added Leaf Envryption Helper [BETA]
+- Added Leaf Password Helper [BETA]
+- Added secret key for token encryption in Leaf Authentication
+
+### Fixed
+
+- Fixed Request::params
+- Fixed Request::getBody
+- Fixed Request Method Tests
+- Fixes to Auth::validateToken
+- Fixed bugs with Leaf DB packages
+- Fixed bugs on Auth::login and register
+- Fixed base64 security issues on Leaf Token [BETA]
+- Fixes on Form::isEmpty and isNull
+
+### Changed
+
+- Renamed Request `getBody` to `body`
+- Switched all `Date` methods to `snake_case`
+- Switched `FS` methods to `snake_case`
+- Shortened `Date` method names (Find out more in the [docs](https://leafphp.netlify.com/))
+- Made Leaf Authentication a helper (Leaf\Helper\Authentication)
+
+### Removed
+
+- Removed Response::count
+- Removed Response::getIterator
+- Removed Response header offeset methods
+
+## v2.0 - beta - 11th March, 2020
+
+### Added
+
+- Added DB->choose
+- Added DB->add
+- Added Auth->login
+- Added Auth->register
+- Added Session->unset
+- Added custom constructor to response
+- Added Response->messages(Http codes)
+- Added Response->setStatus/getStatus/status
+- Added Response->setHeader/getHeader/header
+- Added Response->setCookie/deleteCookie
+- Added Response->redirect
+- Added Request type checks
+- Added Request->cookies
+- Added Request->headers
+- Added Response Helpers
+- Added Leaf\Headers
+- Added Leaf\Cookies
+- Added ContentTypes Middleware
+- Added Flash messaging Middleware
+- Added PrettyExceptions Middleware
+- Added Logwriter and Log
+- Added Leaf View
+- Merged the Leaf Veins Templating engine and Leaf Core
+- Added Support for blade templating with Leaf Blade
+- Added support for more request types on Leaf::Request
+- Added Form::validateField
+- Provided security against XSS
+- Added Form::submit
+
+### Fixed
+
+- Fixed SESSION->id
+- Fixed headers bug with Response->respondWithCode
+- Fixed headers bug with Response->throwErr
+
+### Changed
+
+- Changed Leaf\Core namespace to Leaf
+- Changed Session->remove to Session->unset
+
+### Removed
+
+- Removed Auth->basicLogin
+- Removed Auth->emailLogin
+- Removed Auth->basicRegister
+
+## v1.5.0 - 11th December, 2019
+
+### Added
+
+- Added FS->deleteFolder
+- Added FS->deleteFile
+- Added Form->validate😅
+- Added Form->validate and return errors to base controllers
+- Added Leaf\Core\Str: equivalent of Illuminate\Support\Str with added methods
+- Added Leaf Mysqli🤔
+- Added Leaf PDO🤔
+- Added Leaf\Core\Auth: simple login and signup
+
+### Fixed
+
+- Fixed FS->deleteFile
+- Fixed FS->listDir
+- Fixed Leaf DB
+- Fixed init bug with session
+
+### Changed
+
+- Renamed Veins->renderTemplate to render
+- Rename veins->assign to set()
+- Renamed mkdir to createFolder
+- Renamed mkdirInBase to createFolderInBase
+- Renamed renameDir to renameFolder
+- Changed vein file extension from .vein to .vein.html
+- Split Leaf\Config\Db between Leaf\Core\Db\Mysqli and Leaf\Core\Db\PDO
+- Changed `renderHtml` to `renderPage`
+- Changed all `getParam`s to `get`
+
+### Removed
+
+Nothing was removed
+
+## v1.4.2 - 13th November, 2019
+
+### Added
+
+- Added FileSystem module
+- Added `mysqliQuery` method to `leaf\config\db`
+- Added a bunch of handy session methods
+- Added leaf token
+- Added leaf form
+
+### Fixed
+
+- Fixed a few problems with `leaf\config\db`;
+- Fixed tiny bug with `response->throwErr`
+
+### Changed
+
+- Changed `leaf\config\db`: connection variables and connection type are set on db init. `$db = new db($host, $user, $password, $dbname, "PDO")`
+- Renamed renderHtmlPage to renderHtml
+
+### Removed
+
+- Leaf\Config\DB has been depricated for now
+
+## v1.4.1 - 1st November, 2019
+
+### Added
+
+- Added base Leaf Controller `Leaf\Core\Controller`
+- Added base controller for APIs: `Leaf\Core\ApiController`
+- Added base Leaf Model `Leaf\Core\Model`
+- Added support for full MVC app
+- Added [Leaf Veins](https://github.com/leafsphp/veins) in default Leaf package
+- Added Error Handling for development and production
+- Added a base database layer connected with custom environment variables
+
+### Fixed
+
+- Fixed bug with `Response::renderHtmlPage()`
+- Fixed the HTTP code rendering in the browser from `Response::respondWithCode`
+
+### Changed
+
+- Changed `Validation` to `Form`
+
+### Removed
+
+- Removed Leaf `Exceptions`
+- Removed Middleware interfaces
diff --git a/apps/docs/src/coc/index.md b/apps/docs/src/coc/index.md
new file mode 100644
index 0000000..14e64a8
--- /dev/null
+++ b/apps/docs/src/coc/index.md
@@ -0,0 +1,45 @@
+# Code Of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, political party, or sexual identity and orientation. Note, however, that religion, political party, or other ideological affiliation provide no exemptions for the behavior we outline as unacceptable in this Code of Conduct.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+- Using welcoming and inclusive language
+- Being respectful of differing viewpoints and experiences
+- Gracefully accepting constructive criticism
+- Focusing on what is best for the community
+- Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+- The use of sexualized language or imagery and unwelcome sexual attention or advances
+- Trolling, insulting/derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or electronic address, without explicit permission
+- Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mychi@leafphp.dev. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
diff --git a/apps/docs/src/codelabs/contributing.md b/apps/docs/src/codelabs/contributing.md
new file mode 100644
index 0000000..8c7e3de
--- /dev/null
+++ b/apps/docs/src/codelabs/contributing.md
@@ -0,0 +1,94 @@
+
+
+# Contributing
+
+## What we are looking for
+
+The Codelab section gives developers examples to work off of that both cover common or interesting use cases, and also progressively explain more complex detail. Our goal is to move beyond a simple introductory example, and demonstrate concepts that are more widely applicable, as well as some caveats to the approach.
+
+If you're interested in contributing, please initiate collaboration by filing an issue under the tag **`codelabs experiment`** with your concept so that we can help guide you to a successful pull request. After your idea has been approved, please follow the template below as much as possible. Some sections are required, and some are optional. Following the numerical order is strongly suggested, but not required.
+
+Experiments should generally:
+
+- Solve a specific, common problem
+- Start with the simplest possible example
+- Introduce complexities one at a time
+- Link to other docs, rather than re-explaining concepts
+- Describe the problem, rather than assuming familiarity
+- Explain the process, rather than just the end result
+- Explain the pros and cons of your strategy, including when it is and isn't appropriate
+- Mention alternative solutions, if relevant, but leave in-depth explorations to a separate experiment
+
+We request that you follow the structure below. We understand, however, that there are times when you may necessarily need to deviate for clarity or flow.
+
+### What Are We Building? required
+
+1. Define the problem you are trying to solve in a few sentences.
+2. Explain the simplest possible solution in a sentence or two.
+3. Show a small code sample.
+
+### Pre-requisites required
+
+1. List any pre-requisites for this pattern to work.
+2. Be sure to link the relevant documentation.
+
+### Setup for the tutorial required
+
+Demonstrate how to setup a project for your experiment. This is required for every experiment.
+
+### Cautions optional
+
+It's extremely helpful to write add a section about any potential pitfalls or gotchas. This is especially important for more advanced experiments. If you're not sure what to write here, you can leave it out.
+
+### What to avoid optional
+
+PHP is a language with many ways to solve a problem, and it's important to explain why you've chosen the approach you have. This section should be used to explain why other approaches are not recommended. This section is not required, but heavily recommended.
+
+### Custom titles optional
+
+You can use custom titles for your sections if you feel it helps the flow of your experiment. This is not required, but can be helpful.
+
+## Template
+
+We've provided a template below that you can copy and paste into a new file. You can add the details in the sections as you see fit. If you have any questions, please file an issue and we'll be happy to help. If you have an idea for a codelab, please file an issue with the tag **`codelabs experiment`**.
+
+```md
+# Experiment Title
+
+::: warning Version support
+Version support. You should also add if it requires a particular PHP version.
+:::
+
+## What Are We Building?
+
+You can give an overview followed by a real world example of what you will be doing and how it helps.
+
+::: details (For beginners)
+You can add beginner info in collapsible blocks like this so advanced users don't have to read everything.
+:::
+
+## Pre-requisites
+
+You need to know this and that to follow this tutorial.
+You need to have this and that installed.
+
+## Setup for the Tutorial
+
+Add this section only if this tutorial requires a special setup.
+
+## Cautions (if any)
+
+Remember to link any relevant document or resource.
+
+## (Your title for body)
+
+You can use subtitles and anything you need to convey your tutorial effectively to the user.
+
+
+
+Experiment by **Name**
+```
+
+## Thank you
+
+It takes time to contribute to documentation, and if you spend the time to submit a PR to this section of our docs, you do so with our gratitude.
diff --git a/apps/docs/src/codelabs/experiments/auth/api/index.md b/apps/docs/src/codelabs/experiments/auth/api/index.md
new file mode 100755
index 0000000..de1675a
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/auth/api/index.md
@@ -0,0 +1,345 @@
+# API Auth
+
+::: warning Version support
+This experiment supports `v3.0` upwards.
+:::
+
+## What Are We Building?
+
+Modern web app conventions have led to a lot of web apps relying on AJAX requests to a backend (API) using libraries like [axios](https://github.com/axios/axios). These backend APIs take in json encoded data from the frontend, perform some operations and send back a response.
+
+In this experiment, we'll be looking at how to create logins, signups and user updates with Leaf v3.0 +. We'll have various requests in JSON form, and some with headers which we'll be using for this experiment.
+
+::: details Detailed Explanation: A quick look at APIs
+*If you know what an API is, you can skip this part*
+
+***What is an API?***
+
+> An Application Programming Interface (API) allows two systems to communicate with one another. - smartbear.com
+
+For our use case, we can say that our API is the backend of whatever application we are building. Our frontend handles user interactions, however, if the frontend needs to perform an operation that needs data from our database for instance, it sends a request to our backend (API) which then checks and returns a response to our frontend. The frontend then performs an action based on whatever response the API returns.
+:::
+
+::: details Detailed Explanation: JWT Auth Concept
+*If you know how JWTs work, you can skip this part*
+
+***What is a JWT?***
+
+> JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. - jwt.io
+
+Basically, you can see this as hashed token containing some specified information token locked with a secret key. JWTs also have expiration times, which means that you can issue a token for a particular timeframe.
+
+***JWTs in user authentication***
+
+- A user's information will be submited to our API
+- We check to the credentials against our database and return the user
+- We take the encode some unique user info into the JWT and return that to the frontend
+
+These steps will be explained into details below. You can check the [JWT spec](https://jwt.io/introduction) for more info.
+:::
+
+## Prerequisites
+
+- To get started with this tutorial, you will first need to set up a leaf 3 application. The easiest way to do this is to use the [Leaf CLI](https://cli.leafphp.dev).
+
+```sh
+leaf create test-app --basic --v3
+```
+
+This will generate a simple leaf 3 app with an `index.php` file as well.
+
+- You will also need basic knowledge on [leaf's router](https://leafphp.dev/docs/routing/)
+
+## Building Our Login
+
+Our authentication flow starts with a user entering their credentials on the frontend of an app. This is parsed and sent to the backend (our API) through an [API endpoint](https://smartbear.com/learn/performance-monitoring/api-endpoints/) or route. This means that our first task is to set up a route to handle the data passed into our API.
+
+We'll use [leaf's core router](https://leafphp.dev/docs/routing/) for this.
+
+```php
+require __DIR__ . "/vendor/autoload.php";
+
+app()->post("/login", function () {
+ // login stuff here
+});
+
+app()->run();
+```
+
+Before we move on, let's give our data from the frontend a structure so we know exactly what we are working with. This app will take in a username and a password defined in this way.
+
+```js
+{
+ "username": "mychi",
+ "password": "password"
+}
+```
+
+Our next task is to get the data passed into our app and cross-check it with the data in our database. If the user credentials are found and match those in our database, we will generate a JWT and attach it to the response along with the user, otherwise, we throw an error.
+
+We can retrieve the `username` and `password` passed into our app using the leaf request object.
+
+```php
+$data = request()->get(["username", "password"]);
+```
+
+This grabs the username and password from the request and saves them in the `$data` variable.
+
+Now, we need to tackle the part that checks the database for the current credentials and generates a JWT if the user is found. Leaf provides an auth module to simply and quickly tackle this. To proceed, we will need to install the auth module.
+
+Composer:
+
+```sh
+composer require leafs/auth
+```
+
+Or with leaf CLI:
+
+```sh
+leaf install auth
+```
+
+After this, we should automatically have access to the `auth()` global.
+
+::: tip Using leaf auth
+When using Leaf auth in a leaf app, you don't need to work with classes and namespaces at all since all of the auth functionality is available on the `auth()` method. You don't even need to initialize it.
+:::
+
+Now, we have our auth module installed, but we still need to connect to our database before we can work with authentication:
+
+```php
+auth()->connect("host", "user", "password", "dbName");
+```
+
+eg:
+
+```php
+auth()->connect("127.0.0.1", "root", "", "appDB");
+```
+
+Now that we're done with that, let's check our database and generate our token. As usual, leaf provides a simple way of doing this.
+
+```php
+$res = auth()->login("users", $data);
+```
+
+This single line of code checks for a user in our database, prepares an error if authentication fails, and finally returns the user with a generated JWT if the authentication is successful.
+
+When the authentication is successful, the `$res` variable above will have the user and the generated JWT. But what happens when authentication fails? When authentication fails, the `$res` variable will be `null`. But how do we know what exactly went wrong? Simple! We can use the `errors` method on leaf auth:
+
+```php
+$res = auth()->login("users", $data);
+
+if ($res === null) {
+ response()->throwErr([
+ "status" => "failed",
+ "data" => auth()->errors(),
+ ], 401);
+}
+```
+
+We are using `response()->throwErr` to break the app with a specified response. This will make sure that the code below it doesn't run just like adding a `return` statement does.
+
+This handles the error response, but if the authentication is successful, the `$res` variable will contain the user and the generated JWT as mentioned above.
+
+```php
+$res = auth()->login("users", $data);
+
+if ($res === null) {
+ response()->throwErr([
+ "status" => "failed",
+ "data" => auth()->errors(),
+ ], 401);
+}
+
+response()->json([
+ "status" => "success",
+ "data" => $res,
+]);
+```
+
+So, putting all of this together, we'll have something like this:
+
+```php
+connect("host", "user", "password", "dbName");
+auth()->config("PASSWORD_ENCODE", function($password) {
+ return \CustomPasswordHash::create($password);
+});
+
+app()->post("/login", function () {
+ $data = request()->get(["username", "password"]);
+ $res = auth()->login("users", $data);
+
+ if ($res === null) {
+ response()->throwErr([
+ "status" => "failed",
+ "data" => auth()->errors(),
+ ]);
+ }
+
+ response()->json([
+ "status" => "success",
+ "data" => $res,
+ ]);
+});
+
+app()->run();
+```
+
+The output will look like this:
+
+```js
+{
+ "user": {
+ "username": "mychi",
+ "email": "mickdd22@gmail.com",
+ "created_at": "2019-09-20 13:47:48"
+ },
+ "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NzYxMzUzMjgsImlzcyI6ImxvY2FsaG9zdCIsImV4cCI6MTU3NjEzNjIyOCwidXNlcklkIjoxfQ.7FODXGGJKioGQVX4ic0DJLoMIQTVUlsd4zFAJA4DAkg"
+}
+```
+
+## Using our login
+
+After creating our login handler, we just need to plug this into our frontend. We can do this using any library for handling network requests. For this example, we will use [axios](https://github.com/axios/axios)
+
+::: tip Note
+For this demonstration, we will be using axios with promises. Take a look at ES6 if you are not familiar with promises.
+:::
+
+```js
+axios.post('/login', {
+ username: 'mcyhi',
+ password: 'password',
+}).then((res) => {
+ // success login instance
+ if (res.data.status === "success") {
+ localStorage["user"] = res.data.data.user;
+ localStorage["token"] = res.data.data.token;
+ }
+}).catch((err) => {
+ if (err?.response && err?.response?.data?.status) {
+ // a toast library
+ toast.error(err?.response?.data?.auth);
+ }
+});
+```
+
+## Building Our Register
+
+It's this easy to create and use logins, so what about registrations? Even easier! Registration involves saving the user in the database, if the user is returned immedietely, a token needs to be created as well. Since we've already configured Leaf Auth, let's just jump right into the code.
+
+```php{3}
+app()->post("/register", function () {
+ $data = request()->get(["username", "email", "password"]);
+ $res = auth()->register("users", $data, ["username", "email"]);
+
+ if ($res === null) {
+ response()->throwErr([
+ "status" => "failed",
+ "data" => auth()->errors(),
+ ]);
+ }
+
+ response()->json([
+ "status" => "success",
+ "data" => $res,
+ ]);
+});
+```
+
+The third line calls the `register` function which is responsible for registering the user. Unlike `login` above, we passed in a third parameter: an array of items which need to be unique in the database. In this case, leaf auth will make sure that there is no same `username` or `email` in the database before saving the user's credentials.
+
+## Building Our Update User
+
+To edit a user, we have to find the user we want to edit. This means that the user should be logged in. Since we're using JWT, we'll need to pass the token into the request as a bearer token in the authorization header.
+
+An example of this with axios will look like this:
+
+```js{7}
+import axios from "axios";
+
+axios({
+ url: `${API_URL}/update`,
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ ...
+})
+```
+
+Back on our system, we'll have to detect the token, grab and decode it, find the data encoded into it and retrieve the user id. Sounds complex, but again, one line of code with Leaf.
+
+```php
+$userId = auth()->id() ?? response()->throwErr([
+ "status" => "failed",
+ "data" => auth()->errors(),
+]);
+```
+
+Based on this id, we can create a condition to find the user we want to update. This condition will be passed together with our params into the `update` method, which will look like this:
+
+```php
+$data = request()->get(["username", "email"]);
+
+// credentials to find user by
+$where = ["id" => auth()->id() ?? response()->throwErr(auth()->errors())];
+
+// unique data
+$uniques = ["username", "email"];
+
+$user = auth()->update("users", $data, $where, $uniques);
+```
+
+## Putting it all together
+
+```php
+connect("host", "user", "password", "dbName");
+auth()->config("PASSWORD_ENCODE", function ($password) {
+ return \CustomPasswordHash::create($password);
+});
+
+app()->post("/login", function () {
+ $data = request()->get(["username", "password"]);
+ $res = auth()->login("users", $data);
+
+ if (!$res) {
+ response()->throwErr(auth()->errors());
+ }
+
+ response()->json($res);
+});
+
+app()->post("/register", function () {
+ $data = request()->get(["username", "email", "password"]);
+ $res = auth()->register("users", $data, ["username", "email"]);
+
+ if (!$res) {
+ response()->throwErr(auth()->errors());
+ }
+
+ response()->json($res);
+});
+
+app()->post("/update", function () {
+ $data = $request->get(["username", "email"]);
+ $where = ["id" => auth()->id() ?? response()->throwErr(auth()->errors())];
+
+ $user = auth()->update("users", $data, $where, ["username", "email"]);
+});
+
+app()->run();
+```
+
+
+
+Experiment by Mychi Darko
diff --git a/apps/docs/src/codelabs/experiments/auth/index.md b/apps/docs/src/codelabs/experiments/auth/index.md
new file mode 100644
index 0000000..53c81b4
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/auth/index.md
@@ -0,0 +1,12 @@
+---
+aside: none
+---
+
+# Authentication
+
+This is a collection of codelab experiments related to web/API authentication across different platforms. Since there are multiple setups of leaf, along with Leaf MVC and Leaf API, this page will contain variations to give you the best experience for the version you're using.
+
+| Topic | Description |
+| :------------------------------------------------- | :----------------------------------------------------------------- |
+| [API auth](/codelabs/experiments/auth/api/) | API authentication with base Leaf 3 |
+| [Session auth](/codelabs/experiments/auth/session/) | Session authentication with base Leaf 3 |
diff --git a/apps/docs/src/codelabs/experiments/auth/session/index.md b/apps/docs/src/codelabs/experiments/auth/session/index.md
new file mode 100755
index 0000000..d6d9ab4
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/auth/session/index.md
@@ -0,0 +1,266 @@
+# Session Auth
+
+::: warning Version support
+This experiment supports `v3.0` upwards.
+:::
+
+## What are we building?
+
+Although most web apps mostly rely on APIs and token/OAuth, the practice of using built in sessions for authentication certainly hasn't faded out.
+
+In this experiment, we'll be looking at how to create logins, signups and user updates with Leaf v3. We'll be relying on session support in the auth package and we'll also be using blade for our templating.
+
+::: details Detailed Explanation: Session authentication
+Session based authentication is one in which the user state is stored on the server’s memory.
+
+Read the full article [here](https://dev.to/thecodearcher/what-really-is-the-difference-between-session-and-token-based-authentication-2o39)
+:::
+
+::: details Detailed Explanation: Templating engines
+For the sake of our tutorial, let's say that templating engines are a way of outputting code which allows us separate our logic from our views. There are many template engines out there, blade, twig, smarty and more.
+
+According to expressjs.com:
+
+> a template engine enables you to use static template files in your application. At runtime, the template engine replaces variables in a template file with actual values, and transforms the template into an HTML file sent to the client. This approach makes it easier to design an HTML page.
+:::
+
+## Building Our Login
+
+First of all, we need a route to handle our login screen. We'll use leaf's core router for this. To get started, we'll need to install leaf auth and leaf blade. We can do this with composer:
+
+```sh
+composer require leafs/auth
+composer require leafs/blade
+```
+
+or with leaf CLI:
+
+```sh
+leaf install auth
+leaf install blade
+```
+
+After this, we can create our `index.php` file:
+
+```php
+require __DIR__ . "/vendor/autoload.php";
+
+// show login page
+app()->get("/auth/login", function() {
+ echo app()->blade->render("login");
+});
+
+// login logic
+app()->post("/auth/login", function() {
+ // logic here
+});
+
+app()->run();
+```
+
+Next we'll define our blade view, `login.blade.php`:
+
+```php
+
+
Login
+
+ Sign into Leaf APP
+
+
+
+```
+
+Before we jump into the code, let's talk about the processes involved. When a user logs in, a new session is created for them. This session holds the user and the session tracker logs assigned by Leaf Auth. We can check the user's logged in state to load a page or redirect.
+
+To set all this up with Leaf, all we need to do is to tell leaf auth to use sessions instead of just JWTs.
+
+```php
+auth()->useSession();
+```
+
+or
+
+```php
+auth()->config("USE_SESSION", true);
+```
+
+::: tip Functional Mode ⚡️
+Note that the `auth` function we are using above comes from the auth package extending leaf 3's functional mode.
+:::
+
+```php
+require __DIR__ . "/vendor/autoload.php";
+
+auth()->useSession();
+
+app()->get("/auth/login", function() {
+ app()->blade->render("login");
+});
+
+app()->post("/auth/login", function() {
+ // logic here
+});
+```
+
+Now we can focus on the login logic. If you've used auth before in previous versions, adding session support literally changed nothing about the syntax, so you can still comfortably create your logins in the very same way.
+
+```php
+auth()->login("users", [
+ "username" => $username,
+ "password" => $password
+]);
+```
+
+Even the error handling works the same way too:
+
+```php
+$user = auth()->login("users", [
+ "username" => $username,
+ "password" => $password
+]);
+
+if (!$user) {
+ // an error happened here, you can display
+ // them on your template. The errors can be
+ // grabbed with auth()->errors()
+}
+```
+
+On a successful login, `login` will redirect the user to `GUARD_HOME` which is simply the homepage route. This route can be configured using the config method.
+
+```php
+auth()->config("GUARD_HOME", "/dashboard");
+```
+
+Putting it all together, we can have something like this:
+
+```php
+// get username and password submitted from login form
+list($username, $password) = array_values(request(["username", "email"]));
+
+auth()->config("GUARD_HOME", "/dashboard");
+
+// attempt a login
+$user = auth()->login("users", [
+ "username" => $username,
+ "password" => $password
+]);
+
+// if there's a problem with credentials or system
+if (!$user) {
+ // render login page
+ return app()->blade->render("login", [
+ // pass in errors and credentials
+ "errors" => auth()->errors(),
+ "username" => $username,
+ "password" => $password,
+ ]);
+}
+```
+
+As you can see nothing has changed in the login implementation even though a ton of new features have been added. Restructuring the code, we'll have something like this. There's one last thing though. When an already logged in user tries to go to the login page, we want to redirect to `GUARD_HOME`.
+
+This means we have to check the session to find if the user is logged in or not, redirect if the user is logged in and maintain the page if the user isn't. Leaf makes this simple as well as it provides a `guard` method.
+
+```php
+auth()->guard("guest");
+```
+
+This tells leaf that the page is a guest page and should not be accesible when the user is logged in. Putting all what we've discussed together should look like this:
+
+```php
+require __DIR__ . "/vendor/autoload.php";
+
+// connect to db. You can use autoConnect too
+auth()->connect("host", "user", "password", "dbName");
+auth()->config("GUARD_HOME", "/dashboard");
+
+app()->get("/auth/login", function() {
+ auth()->guard("guest");
+
+ app()->blade->render("login");
+});
+
+app()->post("/auth/login", function() {
+ auth()->guard("guest");
+
+ list($username, $password) = array_values(app()->request()->get(["username", "email"]));
+
+ // attempt a login
+ $user = auth()->login("users", [
+ "username" => $username,
+ "password" => $password
+ ]);
+
+ // if there's a problem with credentials or system
+ if (!$user) {
+ // render login page
+ return app()->blade->render("login", [
+ // pass in errors and credentials
+ "errors" => auth()->errors(),
+ "username" => $username,
+ "password" => $password,
+ ]);
+ }
+});
+```
+
+## Building Our Register
+
+Registration involves saving the user in the database. From there we can immedietely initialize a session or go to the `GUARD_LOGIN` page so the user signs in. Since we've already configured Leaf Auth, let's just jump right into the code.
+
+```php
+app()->post("/auth/register", function() {
+ auth()->guard("guest");
+
+ $credentials = request()->get(["username", "email", "password"]);
+
+ // automatically login immedietely the user is created
+ auth()->config("SESSION_ON_REGISTER", true);
+
+ $user = auth()->register("users", $credentials, [
+ "username", "email"
+ ]);
+
+ if (!$user) {
+ return app()->blade->render("register", array_merge(
+ ["errors" => array_merge(is()->auth->errors()],
+ request(["username", "email", "password"]))
+ );
+ }
+});
+```
+
+Here, we're creating a handler for our register method, getting the request data we need and saving it in the database using `register`. You might have noticed the 3rd parameter, `["username", "email"]`. This just makes sure that the same username and email don't already exist in the database. Leaf literally does everything for you. We can have a template like this:
+
+```php
+
+```
+
+And with this we've successfully created our register functionality. Read the auth docs for more info on session auth.
+
+Experiment by Mychi Darko
diff --git a/apps/docs/src/codelabs/experiments/caching/index.md b/apps/docs/src/codelabs/experiments/caching/index.md
new file mode 100644
index 0000000..d78866d
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/caching/index.md
@@ -0,0 +1,7 @@
+---
+aside: none
+---
+
+# Caching
+
+This is a collection of codelab experiments related to caching across different platforms.
diff --git a/apps/docs/src/codelabs/experiments/deployment/digitalocean/index.md b/apps/docs/src/codelabs/experiments/deployment/digitalocean/index.md
new file mode 100644
index 0000000..5f4c847
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/deployment/digitalocean/index.md
@@ -0,0 +1,152 @@
+# Deploying a LeafMVC Application to Digital Ocean
+
+::: warning Version support
+Version support. This tutorial assumes use of LeafPHP >= 3.0 and PHP >=7.0.
+:::
+
+## What Are We Building
+
+This experiment will guide you deploying your first LeafMVC application to Digital Ocean. A majority
+of the same steps apply to Leaf v3 core as well.
+
+::: details (New to Digital Ocean?)
+Digital Ocean is a cloud service provider that offers great introductory pricing for virtual private
+servers (VPS). Create an account, tether a credit card, and prepare to build.
+:::
+
+## Prerequisites
+
+Before continuing, it is important to determine if you would like to purhcase or point a domain name
+to the VPS you are about to spin up. $DOMAIN will be shown several times throughout this experiment
+and should be replaced by either your domain name (example.com) or the Droplet's public IP address. You
+can grab the public IP address from the Digital Ocean control panel.
+
+For instructions on how to setup a domain with Digital Ocean, [click here](https://docs.digitalocean.com/products/networking/dns/how-to/add-domains/).
+
+## 1. Create a new droplet
+
+From the control panel, click the green "Create" button and select droplet. We will create a VPS with the
+following options selected:
+
+* Ubuntu: 20.04 (LTS)
+* Plan: Basic
+* CPU Options: Premium AMD or Regular Intel
+* $6/mo package
+
+::: tip Scaling ⚡️
+Should your application grow in requirements or traffic, you can always come back and increase your package selection.
+:::
+
+### Authentication
+
+It is highly recommended that your utilize SSH-based authentication. Select an existing key, or [generate a new key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent), then add it.
+
+## 2. Initial droplet setup
+
+After your droplet has been created, you will need to login, secure it, and install required software. The first task will
+be to create an admin user, then utilie that account for future SSH connections.
+
+```bash
+ssh root@$DOMAIN
+adduser username
+usermod -aG sudo username
+rsync --archive --chown=username:username ~/.ssh /home/username
+```
+
+Test the admin account: ``su - username``. If the command executes, you can terminal the SSH session and log
+back in with your new user account (recommended).
+
+### Setup firewall
+
+Next we will setup UFW - Ubuntu Firewall. We will allow communication on ports: 22 (SSH), 80 (HTTP), and 443 (SSL).
+
+```bash
+sudo ufw allow 22
+sudo ufw allow 80
+sudo ufw allow 443
+sudo ufw enable
+```
+
+After creating the firewall's rules and enabling UFW, you can view firewall status by ``sudo ufw status``.
+
+### Install required software
+
+It is now time install all of the needed software to enable LeafPHP to run. First, we need to update all system software:
+
+```bash
+sudo apt update
+sudo apt upgrade
+```
+
+Be sure to respond **Y** when asked to continue. Now we can intall NGINX, PHP, MySQL, and curl.
+
+```bash
+sudo apt install nginx php-fpm php-mysql php-curl
+```
+
+Once complete, follow the
+[NGINX instructions](https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04#step-3-%E2%80%93-installing-php-and-configuring-nginx-to-use-the-php-processor).
+Ensure that your directory is set as such: `` root /var/www/$DOMAIN/public;`` Below is an example sites-available file.
+
+```nginx
+server {
+ server_name itsglint.com www.itsglint.com 147.182.136.153;
+ root /var/www/itsglint.com/public;
+
+ index index.html index.htm index.php;
+
+ location / {
+ try_files $uri /index.php?$query_string;
+ }
+
+ location ~ \.php$ {
+ include snippets/fastcgi-php.conf;
+ fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
+ }
+}
+```
+
+::: warning Leaf Router and .htaccess support
+It is important to mirror the location blocks as-in. Otherwise, LeafRouter will not work properly or at all.
+:::
+
+Next, we will install Mysql. Follow the [install instructions](https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04#step-2-%E2%80%93-installing-mysql-to-manage-site-data).
+
+```bash
+sudo apt install mysql-server
+```
+
+### Install your Leaf🍁 application
+
+Next, we will download and install our application with all dependencies. Clone your repository from Github,
+or any source, and place your Leaf project in ``/var/www/$DOMAIN``. Afterwards, install required dependencies
+and perform initial Leaf tasks:
+
+```bash
+composer install
+php leaf db:install
+php leaf db:migrate
+```
+
+You also may seed the database if required: `php leaf db:seed`.
+
+Congratulations 🎉, you now have a fully working production server, and should be able to reach your application at $DOMAIN.
+
+::: details Recommended: Complete SSL Setup
+If your Leaf applications is more than a hobbyist adventure and serving actual clients or visitors, it is
+strongly recommended to complete the SSL setup. SSL encrypts traffic between a browser and server. Replace
+example.com with $DOMAIN.
+
+```
+sudo apt install certbot python3-certbot-nginx
+sudo systemctl reload nginx
+sudo certbot --nginx -d example.com -d www.example.com
+```
+
+When prompted for HTTPS redirction, select **Option 2**, forcing HTTPS traffic.
+
+:::
+
+
+
+Experiment by **[Matthew Reichardt](https://github.com/matthewjamesr)**
diff --git a/apps/docs/src/codelabs/experiments/deployment/heroku/index.md b/apps/docs/src/codelabs/experiments/deployment/heroku/index.md
new file mode 100644
index 0000000..5687048
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/deployment/heroku/index.md
@@ -0,0 +1,61 @@
+# Deploying a Leaf Application to Heroku
+
+::: warning Version support
+Version support. This tutorial assumes use of Leaf CLI >= 2.2.0 (🍊 Yomi Yomi no Mi).
+:::
+
+## What Are We Building?
+
+This experiment will guide you deploying your first Leaf application to Heroku. The same steps apply to Leaf MVC, Leaf API and Skeleton.
+
+::: details (New to Heroku?)
+Heroku is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud. Create an account, tether a credit card, and prepare to build.
+:::
+
+## Prerequisites
+
+This tutorial assumes you have the following:
+
+- A Leaf application
+- A Heroku account
+- The Heroku CLI installed
+
+## 1. Create a new heroku app
+
+From the dashboard, select create new app. Give your app a name, and select a region. The region should be the closest to your location.
+
+
+
+**Note that per Heroku's new terms, you should have a payment method connected.**
+
+Clicking on create app, should take you to an empty new app page. From here, you can select the deploy tab, and connect your Github repository.
+
+
+
+## 2. Initializing your app
+
+For most use-cases, you would usually push your project to GitHub, then connect your repository to Heroku. However, for this experiment, we will be pushing your app to Heroku directly using the Leaf CLI.
+
+To get started, you will need to make sure you are logged in to Heroku. You can do this by running `heroku login`. You will be prompted to login in your browser. Once you have logged in, you can proceed.
+
+Heroku uses git to deploy your application, so you will need to initialize git in your repository. You can do this by running `git init` in your project directory. After initializing git, you can add your files to the staging area by running `git add .`. Once you have added your files, you can commit them by running `git commit -m "commit message"`.
+
+## 3. Deploying your app
+
+Now for the interesting part. We can deploy our application using Leaf CLI's `deploy` command.
+
+```bash
+leaf deploy --to heroku --project your-project-name
+```
+
+For this example, our command would look like this:
+
+```bash
+leaf deploy --to heroku --project leafcodelabs
+```
+
+This command will setup the Heroku remote, build your app and push it to your Heroku project. Once the command is complete, you should see your deploy in the `Deploys` tab on the heroku dashboard.
+
+
+
+Experiment by **[Michael Darko](https://github.com/mychidarko)**
diff --git a/apps/docs/src/codelabs/experiments/deployment/index.md b/apps/docs/src/codelabs/experiments/deployment/index.md
new file mode 100644
index 0000000..8c0ffd9
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/deployment/index.md
@@ -0,0 +1,8 @@
+# Deployment
+
+This is a collection of codelab experiments related to deployment across different platforms.
+
+| Provider | Description |
+| :-------------------------------------------------------------- | :----------------------------------------------------------------- |
+| [Digital Ocean](/codelabs/experiments/deployment/digitalocean/) | Deploying LeafMVC projects to a new Digital Ocean droplet |
+| [Heroku](/codelabs/experiments/deployment/heroku/) | Deploying a base Leaf project to Heroku using the Leaf CLI |
diff --git a/apps/docs/src/codelabs/experiments/index.md b/apps/docs/src/codelabs/experiments/index.md
new file mode 100644
index 0000000..1a08d3c
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/index.md
@@ -0,0 +1,26 @@
+---
+aside: none
+---
+
+# Experiments
+
+A codelab experiment is a single tutorial that focuses on a particular topic. It is interactive and in-depth covering what is needed in full.
+
+Below is a list of experiment topics.
+
+## Topics
+
+| Topic | Description |
+| :------------------------------------ | :----------------------------------------------------------------- |
+| [Hosting](/codelabs/experiments/deployment/) | Experiments related to web/API hosting |
+| [Auth](/codelabs/experiments/auth/) | Experiments related to web/API authentication |
+
+
+
+## Contributing
+
+If you don't find your topic here, you can simply create a pull request with your experiment in the topic folder you need. Find the full contribution guide [here](/codelabs/contributing)
diff --git a/apps/docs/src/codelabs/experiments/javascript/index.md b/apps/docs/src/codelabs/experiments/javascript/index.md
new file mode 100644
index 0000000..c024842
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/javascript/index.md
@@ -0,0 +1,7 @@
+---
+aside: none
+---
+
+# JavaScript
+
+This is a collection of codelab experiments related to JavaScript across different platforms.
diff --git a/apps/docs/src/codelabs/experiments/mail/dynamic-mail-templating/README.md b/apps/docs/src/codelabs/experiments/mail/dynamic-mail-templating/README.md
new file mode 100755
index 0000000..bbc6d91
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/mail/dynamic-mail-templating/README.md
@@ -0,0 +1,105 @@
+# Dynamic Emails With Templating
+
+## Base Example
+
+Writing and designing mails everytime you want to send a message to a user can be a major pain. One solution to this is to have an HTML or PHP script written as an email body, but of course this is very messy and unpractical.
+
+In this codelab experiment, we'll be using Leaf Mail, leaf's mail managing package in addition to Leaf Blade templating engine to send dynamic emails.
+
+So first we'll to prepare our system, initialise any needed package before we continue.
+
+```php
+
+
+
+
+
+ {{ $title }}
+
+
+
+
Dear {{ $name }},
+
+ We are glad to announce that you have been employed as {{ $position }} at Leaf PHP
+
+
+ Love, The Leaf Team.
+
+
+
+
+```
+
+### The System
+
+Basically, we've created a view which takes in a `$title`, `$name` and `$position`. So, back to our main system, we have to fill these fields and save the whole view into our mail body. Let's see how that works.
+
+**Be sure to read Leaf Blade docs and Leaf Mail docs.**
+
+```php
+smtp_connect("host", 0000, true, "username", "password");
+
+$mail->Subject = "Subject";
+$mail->Body = "BLADE VIEW HERE";
+```
+
+So as you can see, all we have to do is to load the blade view into the `Body` section. That's actually all there is to it. Now let's work with blade a bit. We'll have to configure the directories for our blade views, and their compiled files too, after that, we render and pass in the required variables.
+
+Note that rendering blade views doesn't output that view, it just returns it, to actually output the view, we have to explicitly use `echo` or Leaf's `renderMarkup`. Thus, we're taking advantage of this.
+
+```php
+$app->blade->configure("app/pages", "app/pages/cache");
+$body = $app->blade->render("mail", [
+ "title" => "Employment",
+ "name" => "Michael",
+ "position" => "maintainer"
+]);
+```
+
+Now, our complete view is saved in the `$body` variable, simple right? Now let's patch all what we've done together and send our email.
+
+```php
+blade->configure("app/pages", "app/pages/cache");
+$mail->smtp_connect("host", 0000, true, "username", "password");
+
+$mail->Subject = "Subject";
+$mail->Body = $app->blade->render("mail", [
+ "title" => "Employment",
+ "name" => "Michael",
+ "position" => "maintainer"
+]);
+// other mail fields go here
+$mail->send();
+```
+
+Dont forget to check Leaf Mail's documentation
+
+
+
+Experiment by Mychi Darko
diff --git a/apps/docs/src/codelabs/experiments/mail/index.md b/apps/docs/src/codelabs/experiments/mail/index.md
new file mode 100644
index 0000000..2e1f1e3
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/mail/index.md
@@ -0,0 +1,7 @@
+---
+aside: none
+---
+
+# Mailing
+
+This is a collection of codelab experiments related to mailing.
diff --git a/apps/docs/src/codelabs/experiments/websocket/index.md b/apps/docs/src/codelabs/experiments/websocket/index.md
new file mode 100644
index 0000000..4e8caed
--- /dev/null
+++ b/apps/docs/src/codelabs/experiments/websocket/index.md
@@ -0,0 +1,7 @@
+---
+aside: none
+---
+
+# Websockets
+
+This is a collection of codelab experiments related to websockets across different platforms.
diff --git a/apps/docs/src/codelabs/index.md b/apps/docs/src/codelabs/index.md
new file mode 100644
index 0000000..fa8d14d
--- /dev/null
+++ b/apps/docs/src/codelabs/index.md
@@ -0,0 +1,38 @@
+# Leaf Codelabs
+
+Codelabs is a space we created to give interactive tutorials on Leaf, PHP and JS concepts that help you achieve specific results in your apps. Codelabs takes one topic or "how-to" and dives deep into it, giving you all the information you need to successfully complete the tutorial yourself while also explaining useful concepts along the way.
+
+## Codelabs vs the Docs
+
+How is `codelabs` different from the rest of our documentation? Why is this necessary?
+
+- **Greater Focus**: In the docs, we're essentially telling a story. Each section builds on and assumes knowledge from each previous section. In the codelabs section, each experiment can and should stand on its own. This means experiments can focus on one specific aspect of Leaf, rather than having to give a general overview.
+
+- **Greater Depth**: To avoid making the docs too long, we try to include only the simplest possible examples to help you understand each feature. Then we move on. In the codelabs section, we can include more complex examples, combining features in interesting ways. Each experiment can also be as long and detailed as it needs to be, in order to fully explore its niche.
+
+- **Teaching PHP/JS**: Although our docs are beginner friendly, we still assume basic familiarity with PHP concepts as we do not go over them into details in the docs. For example, we won't explain how forms and network requests work and how data is read. **In codelabs however**, essential PHP/JS features and concepts can be explored and explained in the context of how they help us build better Leaf apps.
+
+- **Hands-on**: Codelabs, unlike the docs packs in interactive hands-on tutorials which you can follow step-by-step. Each tutorial is specially crafted to help you "learn by doing" and is complementary to the docs. For this reason, you will always find links back to leaf, a module or a particular package.
+
+## Experiments
+
+A codelab experiment is a single tutorial that focuses on a particular topic. It is interactive and in-depth, covering what is needed in full.
+
+Below is a list of experiment topics.
+
+| Topic | Description |
+| :------------------------------------------------------ | :------------------------------------------------------------- |
+| [Hosting/Deployment](/codelabs/experiments/deployment/) | Experiments related to web/API deployment |
+
+
+
+
+
+
+
+
+**We are migrating from codelabs.leafphp.dev to leafphp.dev/codelabs. Please bear with us as we move all the experiments here.**
+
+## Contributing
+
+If you don't find your topic here, you can simply create a pull request with your experiment in the topic folder you need. Find the full contribution guide [here](/codelabs/contributing)
diff --git a/apps/docs/src/community/coc.md b/apps/docs/src/community/coc.md
new file mode 100644
index 0000000..fd53230
--- /dev/null
+++ b/apps/docs/src/community/coc.md
@@ -0,0 +1,45 @@
+# Code Of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, political party, or sexual identity and orientation. Note, however, that religion, political party, or other ideological affiliation provide no exemptions for the behavior we outline as unacceptable in this Code of Conduct.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+- Using welcoming and inclusive language
+- Being respectful of differing viewpoints and experiences
+- Gracefully accepting constructive criticism
+- Focusing on what is best for the community
+- Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+- The use of sexualized language or imagery and unwelcome sexual attention or advances
+- Trolling, insulting/derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or electronic address, without explicit permission
+- Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at community@vuejs.org. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
diff --git a/apps/docs/src/community/community-guide.md b/apps/docs/src/community/community-guide.md
new file mode 100644
index 0000000..dbeaa05
--- /dev/null
+++ b/apps/docs/src/community/community-guide.md
@@ -0,0 +1,83 @@
+---
+outline: deep
+---
+
+# Community Guide
+
+Vue's community is growing incredibly fast and if you're reading this, there's a good chance you're ready to join it. So... welcome!
+
+Now we'll answer both what the community can do for you and what you can do for the community.
+
+## Resources
+
+### Code of Conduct
+
+Our is a guide to make it easier to enrich all of us and the technical communities in which we participate.
+
+### Stay in the Know
+
+- Follow our [official Twitter account](https://twitter.com/vuejs).
+- Follow our [team members](./team) on Twitter or GitHub.
+- Follow the [RFC discussions](https://github.com/vuejs/rfcs).
+- Subscribe to the [official blog](https://blog.vuejs.org/).
+
+### Get Support
+
+- [Discord Chat](https://chat.vuejs.org/): A place for Vue devs to meet and chat in real time.
+- [Forum](https://forum.vuejs.org/): The best place to ask questions and get answers about Vue and its ecosystem.
+- [DEV Community](https://dev.to/t/vue): Share and discuss Vue related topics on Dev.to.
+- [Meetups](https://events.vuejs.org/meetups): Want to find local Vue enthusiasts like yourself? Interested in becoming a community leader? We have the help and support you need right here!
+- [GitHub](https://github.com/vuejs): If you have a bug to report or feature to request, that's what the GitHub issues are for. Please respect the rules specified in each repository's issue template.
+- [Twitter Community (unofficial)](https://twitter.com/i/communities/1516368750634840064): A Twitter community, where you can meet other Vue enthusiasts, get help, or just chat about Vue.
+
+### Explore the Ecosystem
+
+- [The Awesome Vue Page](https://github.com/vuejs/awesome-vue): See what other awesome resources have been published by other awesome people.
+- [Vue Telescope Explorer](https://vuetelescope.com/explore): Explore websites made with Vue, with insights on what framework / libraries they use.
+- [Made with Vue.js](https://madewithvuejs.com/): showcases of projects and libraries made with Vue.
+- [The "Show and Tell" Subforum](https://forum.vuejs.org/c/show-and-tell): Another great place to check out what others have built with and for the growing Vue ecosystem.
+
+## What You Can Do
+
+### Help Fellow Users
+
+Code contribution is not the only form of contribution to the Vue community. Answering a question for a fellow Vue user on Discord or the forum is also considered a valuable contribution.
+
+### Help Triage Issues
+
+Triaging an issue means gathering missing information, running the reproduction, verifying the issue's validity, and investigating the cause of the issue.
+
+We receive many issues in [our repositories on GitHub](https://github.com/vuejs) every single day. Our bandwidth is limited compared to the amount of users we have, so issue triaging alone can take an enormous amount of effort from the team. By helping us triage the issues, you are helping us become more efficient, allowing us to spend time on higher priority work.
+
+You don't have to triage an issue with the goal of fixing it (although that would be nice too). Sharing the result of your investigation, for example the commit that led to the bug, can already save us a ton of time.
+
+### Contribute Code
+
+Contributing bug fixes or new features is the most direct form of contribution you can make.
+
+The Vue core repository provides a [contributing guide](https://github.com/vuejs/core/blob/main/.github/contributing.md), which contains pull request guidelines and information regarding build setup and high-level architecture. Other sub-project repositories may also contain its own contribution guide - please make sure to read them before submitting pull requests.
+
+Bug fixes are welcome at any time. For new features, it is best to discuss the use case and implementation details first in the [RFC repo](https://github.com/vuejs/rfcs/discussions).
+
+### Share (and Build) Your Experience
+
+Apart from answering questions and sharing resources in the forum and chat, there are a few other less obvious ways to share and expand what you know:
+
+- **Develop learning materials.** It's often said that the best way to learn is to teach. If there's something interesting you're doing with Vue, strengthen your expertise by writing a blog post, developing a workshop, or even publishing a gist that you share on social media.
+- **Watch a repo you care about.** This will send you notifications whenever there's activity in that repository, giving you insider knowledge about ongoing discussions and upcoming features. It's a fantastic way to build expertise so that you're eventually able to help address issues and pull requests.
+
+### Translate Docs
+
+I hope that right now, you're reading this sentence in your preferred language. If not, would you like to help us get there?
+
+See the [Translations guide](/translations/) for more details on how you can get involved.
+
+### Become a Community Leader
+
+There's a lot you can do to help Vue grow in your community:
+
+- **Present at your local meetup.** Whether it's giving a talk or running a workshop, you can bring a lot of value to your community by helping both new and experienced Vue developers continue to grow.
+- **Start your own meetup.** If there's not already a Vue meetup in your area, you can start your own! Use the [resources at events.vuejs.org](https://events.vuejs.org/resources/#getting-started) to help you succeed!
+- **Help meetup organizers.** There can never be too much help when it comes to running an event, so offer a hand to help out local organizers to help make every event a success.
+
+If you have any questions on how you can get more involved with your local Vue community, reach out on Twitter at [@vuejs_events](https://www.twitter.com/vuejs_events)!
diff --git a/apps/docs/src/community/contributing/doc-style-guide.md b/apps/docs/src/community/contributing/doc-style-guide.md
new file mode 100644
index 0000000..fe553a8
--- /dev/null
+++ b/apps/docs/src/community/contributing/doc-style-guide.md
@@ -0,0 +1,152 @@
+# Documentation Style Guide
+
+This guide will provide an overview of different design elements that are available for your use in creating documentation.
+
+## Alerts
+
+vuepress provides a custom container plugin to create alert boxes. There are four types:
+
+- **Info**: Provide information that is neutral
+- **Tip**: Provide information that is positive and encouraged
+- **Warning**: Provide information that users should be aware of as there is a low to moderate
+- **Danger**: Provide information that is negative and has a high risk to the user
+
+**Markdown Examples**
+
+```
+::: tip
+You can find more information at this site.
+:::
+
+::: tip
+This is a great tip to remember!
+:::
+
+::: warning
+This is something to be cautious of.
+:::
+
+::: danger DANGER
+This is something we do not recommend. Use at your own risk.
+:::
+```
+
+**Rendered Markdown**
+
+::: tip
+You can find more information at this site.
+:::
+
+::: tip
+This is a great tip to remember!
+:::
+
+::: warning
+This is something to be cautious of.
+:::
+
+::: danger DANGER
+This is something we do not recommend. Use at your own risk.
+:::
+
+## Code Blocks
+
+vuepress uses Prism to provide language syntax highlighting by appending the language to the beginning backticks of a code block:
+
+**Markdown Example**
+
+````
+```js
+export default {
+ name: 'MyComponent'
+}
+```
+````
+
+**Rendered Output**
+
+```js
+export default {
+ name: 'MyComponent'
+}
+```
+
+### Line Highlighting
+
+To add line highlighting to your code blocks, you need to append the line number in curly braces.
+
+#### Single Line
+
+**Markdown Example**
+
+````
+```js{2}
+export default {
+ name: 'MyComponent',
+ props: {
+ type: String,
+ item: Object
+ }
+}
+```
+````
+
+**Rendered Markdown**
+
+```js{2}
+export default {
+ name: 'MyComponent',
+ props: {
+ type: String,
+ item: Object
+ }
+}
+```
+
+#### Group of Lines
+
+````
+```js{4-5}
+export default {
+ name: 'MyComponent',
+ props: {
+ type: String,
+ item: Object
+ }
+}
+```
+````
+
+```js{4-5}
+export default {
+ name: 'MyComponent',
+ props: {
+ type: String,
+ item: Object
+ }
+}
+```
+
+#### Multiple Sections
+
+````
+```js{2,4-5}
+export default {
+ name: 'MyComponent',
+ props: {
+ type: String,
+ item: Object
+ }
+}
+```
+````
+
+```js{2,4-5}
+export default {
+ name: 'MyComponent',
+ props: {
+ type: String,
+ item: Object
+ }
+}
+```
diff --git a/apps/docs/src/community/contributing/index.md b/apps/docs/src/community/contributing/index.md
new file mode 100644
index 0000000..c7fae46
--- /dev/null
+++ b/apps/docs/src/community/contributing/index.md
@@ -0,0 +1,78 @@
+# Contribution Guide
+
+
+
+Hi! We're really excited that you are interested in contributing to Leaf. Before submitting your contribution, please make sure to take a moment and read through the following guidelines:
+
+- [Development Setup](#development-setup)
+- [Pull Request Guidelines](#pull-request-guidelines)
+- [Project Structure](#project-structure)
+
+## Development Setup
+
+You will need PHP 7.4 + and [composer](https://getcomposer.org).
+
+After cloning the repo, run:
+
+```bash
+leaf install
+```
+
+Or with composer:
+
+```bash
+composer install
+```
+
+## Pull Request Guidelines
+
+We encourage contributions through pull requests on our repositories. Pull requests are usually of 2 types: bug fixes and feature additions. You can open a "draft" pull request to let the community keep track of work you're doing, however, pull requests will only be evaluated if they are marked as "ready for review" and all tests for new features are passing. Any inactive pull requests left in the "draft" state for an extended period will be closed. The following guidelines should be followed when submitting a pull request:
+
+- The `master` branch is just a snapshot of the latest stable release. All development should be done in dedicated branches. **Do not submit PRs against the `master` branch.**
+
+- Checkout a topic branch from the relevant branch, e.g. `v3.x`, and merge back against that branch.
+
+- It's OK to have multiple small commits as you work on the PR - GitHub will automatically squash it before merging.
+
+- If adding a new feature:
+
+ - Add accompanying test case.
+ - Provide a convincing reason to add this feature. Ideally, you should open a suggestion issue first with the `suggestion` or `feature request` tag and have it approved before working on it.
+
+- If fixing bug:
+ - If you are resolving a special issue, add `(fix #xxxx[,#xxxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `update entities encoding/decoding (fix #3899)`.
+ - Provide a detailed description of the bug in the PR.
+
+## Project Structure
+
+- **`src`**: contains the source code.
+
+ - **`Exception/`**: contains code for the general leaf exceptions.
+
+ This contains default error states and screens for errors, down-times and the like. It also contains extensible mark-up and logging to keep track of errors. Basically, anything relating to errors.
+
+ - **`Helpers/`**: contains default leaf utilities like the container (DI container).
+
+ - **`App.php`**: contains initializers and main leaf code.
+
+ - **`Config.php`**: class for configuring how leaf behaves.
+
+ - **`functions.php`**: Base functions for functional mode.
+
+ - **`Middleware.php`**: base class for creating leaf middleware.
+
+ - **`View.php`**: view config for leaf.
+
+## Financial Contribution
+
+As a pure community-driven project without any corporate backing, we also welcome financial contributions via OpenCollective.
+
+- [Checkout our sponsor page](/support/)
+
+## Credits
+
+Thank you to all the people who have already code to Leaf!
+
+
diff --git a/apps/docs/src/community/contributing/writing-guide.md b/apps/docs/src/community/contributing/writing-guide.md
new file mode 100644
index 0000000..782c458
--- /dev/null
+++ b/apps/docs/src/community/contributing/writing-guide.md
@@ -0,0 +1,111 @@
+# Leaf Docs Writing Guide
+
+Writing documentation is an exercise in empathy. We're not describing an objective reality - the source code already does that. Our job is to help shape the relationship between users and the Leaf ecosystem. This ever-evolving guide provides some rules and recommendations on how to do that consistently within the Leaf ecosystem.
+
+## Principles
+
+- **A feature doesn't exist until it's well documented.**
+- **Respect users' cognitive capacity (i.e. brain power).** When a user starts reading, they begin with a certain amount of limited brain power and when they run out, they stop learning.
+ - Cognitive capacity is **depleted faster** by complex sentences, having to learn more than one concept at a time, and abstract examples that don't directly relate to a user's work.
+ - Cognitive capacity is **depleted more slowly** when we help them feel consistently smart, powerful, and curious. Breaking things down into digestible pieces and minding the flow of the document can help keep them in this state.
+- **Always try to see from the user's perspective.** When we understand something thoroughly, it becomes obvious to us. This is called _the curse of knowledge_. In order to write good documentation, try to remember what you first needed to know when learning this concept. What jargon did you need to learn? What did you misunderstand? What took a long time to really grasp? Good documentation meets users where they are. It can be helpful to practice explaining the concept to people in person before.
+- **Describe the _problem_ first, then the solution.** Before showing how a feature works, it's important to explain why it exists. Otherwise, users won't have the context to know if this information is important to them (is it a problem they experience?) or what prior knowledge/experience to connect it to.
+- **While writing, don't be afraid to ask questions**, _especially_ if you're afraid they might be "dumb". Being vulnerable is hard, but it's the only way for us to more fully understand what we need to explain.
+- **Be involved in feature discussions.** The best APIs come from documentation-driven development, where we build features that are easy to explain, rather than trying to figure out how to explain them later. Asking questions (especially "dumb" questions) earlier often helps reveal confusions, inconsistencies, and problematic behavior before a breaking change would be required to fix them.
+
+## Organization
+
+- **Installation/Integration**: Provide a thorough overview of how to integrate the software into as many different kinds of projects as necessary.
+- **Introduction/Getting Started**:
+ - Provide a less than 10 minute overview of the problems the project solves and why it exists.
+ - Provide a less than 30 minute overview of the problems the project solves and how, including when and why to use the project and some simple code examples. At the end, link to both to Installation page and the beginning of the Essentials Guide.
+- **Guide**: Make users feel smart, powerful, and curious, then maintain this state so that users maintain the motivation and cognitive capacity to keep learning more. Guide pages are meant to be read sequentially, so should generally be ordered from the highest to lowest power/effort ratio.
+ - **Essentials**: It should take no longer than 5 hours to read the Essentials, though shorter is better. Its goal is to provide the 20% of knowledge that will help users handle 80% of use cases. Essentials can link to more advanced guides and the API, though, in most cases, you should avoid such links. When they are provided, you need also provide a context so users are aware if they should follow this link on their first reading. Otherwise, many users end up exhausting their cognitive capacity link-hopping, trying to fully learn every aspect of a feature before moving on, and as a result, never finish that first read-through of the Essentials. Remember that a smooth read is more important than being thorough. We want to give people the information they need to avoid a frustrating experience, but they can always come back and read further, or Google a less common problem when they encounter it.
+ - **Advanced**: While the Essentials helps people handle ~80% of use cases, subsequent guides help get users to 95% of use cases, plus more detailed information on non-essential features (e.g. transitions, animations), more complex convenience features (e.g. mixins, custom directives), and dev experience improvements (e.g. JSX, plugins). The final 5% of use cases that are more niche, complex, and/or prone to abuse will be left to the cookbook and API reference, which can be linked to from these advanced guides.
+- **Reference/API**: Provide a complete list of features, including type information, descriptions of the problem each solves, examples of every combination of options, and links to guides, cookbook recipes, and other internal resources providing more detail. Unlike other pages, this one is not meant to be read top-to-bottom, so plenty of detail can be provided. These references must also be more easily skimmable than the guides, so the format should be closer to dictionary entries than the story-telling format of the guides.
+- **Migrations**:
+ - **Versions**: When important changes are made, it's useful to include a full list of changes, including a detailed explanation of why the change was made and how to migrate their projects.
+ - **From other projects**: How does this software compare to similar software? This is important to help users understand what additional problems we might solve or create for them, and to what extent they can transfer knowledge they already have.
+- **Style Guide**: There are necessarily some key pieces in development that need a decision, but are not core to the API. The style guide provides educated, opinionated recommendations to help guide these decisions. They shouldn't be followed blindly, but can help teams save time by being aligned on smaller details.
+- **Cookbook**: Recipes in the cookbook are written with some assumption of familiarity with Leaf and its ecosystem. Each is a highly structured document that walks through some common implementation details that a Leaf dev might encounter.
+
+## Writing & Grammar
+
+### Style
+
+- **Headings should describe problems**, not solutions. For example, a less effective heading might be "Using props", because it describes a solution. A better heading might be "Passing Data to Child Components with Props", because it provides the context of the problem props solve. Users won't really start paying attention to the explanation of a feature until they have some idea of why/when they'd use it.
+- **When you assume knowledge, declare it** at the beginning and link to resources for less common knowledge that you're expecting.
+- **Introduce only one new concept at a time whenever possible** (including both text and code examples). Even if many people are able to understand when you introduce more than one, there are also many who will become lost - and even those who don't become lost will have depleted more of their cognitive capacity.
+- **Avoid special content blocks for tips and caveats when possible.** It's generally preferable to blend these more naturally into the main content, e.g. by building on examples to demonstrate an edge case.
+- **Don't include more than two interwoven tips and caveats per page.** If you find that more than two tips are needed in a page, consider adding a caveats section to address these issues. The guide is meant to be read straight through, and tips and caveats can be overwhelming or distracting to someone trying to understand the base concepts.
+- **Avoid appeals to authority** (e.g. "you should do X, because that's a best practice" or "X is best because it gives you full separation of concerns"). Instead, demonstrate with examples the specific human problems caused and/or solved by a pattern.
+- **When deciding what to teach first, think of what knowledge will provide the best power/effort ratio.** That means teaching whatever will help users solve the greatest pains or greatest number of problems, with the relatively least effort to learn. This helps learners feel smart, powerful, and curious, so their cognitive capacity will drain more slowly.
+- **Unless the context assumes a string template or build system, only write code that works in any environment by the software (e.g. Leaf, Leafx, etc).**
+- **Show, don't tell.** For example, "To use Leaf on a page, you can add this to your HTML" (then show the script tag), instead of "To use Leaf on a page, you can add a script element with a src attribute, the value of which should be a link to Leaf's compiled source".
+- **Almost always avoid humor (for English docs)**, especially sarcasm and pop culture references, as it doesn't translate well across cultures.
+- **Never assume a more advanced context than you have to.**
+- **In most cases, prefer links between sections of the docs over repeating the same content in multiple sections.** Some repetition in content is unavoidable and even essential for learning. However, too much repetition also makes the docs more difficult to maintain, because a change in the API will require changes in many places and it's easy to miss something. This is a difficult balance to strike.
+- **Specific is better than generic.** For example, a `` component example is better than ``.
+- **Relatable is better than obscure.** For example, a `` component example is better than ``.
+- **Be emotionally relevant.** Explanations and examples that relate to something people have experience with and care about will always be more effective.
+- **Always prefer simpler, plainer language over complex or jargony language.** For example:
+ - "you can use Leaf with a script element" instead of "in order to initiate the usage of Leaf, one possible option is to actually inject it via a script HTML element"
+ - "function that returns a function" instead of "higher order function"
+- **Avoid language that invalidate struggle**, such as "easy", "just", "obviously", etc. For reference, see [Words To Avoid in Educational Writing](https://css-tricks.com/words-avoid-educational-writing/).
+
+### Grammar
+
+- **Avoid abbreviations** in writing and code examples (e.g. `attribute` is better than `attr`, `message` is better than `msg`), unless you are specifically referencing an abbreviation in an API (e.g. `$attrs`). Abbreviation symbols included on standard keyboards (e.g. `@`, `#`, `&`) are OK.
+- **When referencing a directly following example, use a colon (`:`) to end a sentence**, rather than a period (`.`).
+- **Use the Oxford comma** (e.g. "a, b, and c" instead of "a, b and c"). ![Why the Oxford comma is important](/images/oxford-comma.jpg)
+ - Source: [The Serial (Oxford) Comma: When and Why To Use It](https://www.inkonhand.com/2015/10/the-serial-oxford-comma-when-and-why-to-use-it/)
+- **When referencing the name of a project, use the name that project refers to itself as.** For example, "webpack" and "npm" should both use lowercase as that's how their documentation refers to them.
+- **Use Title Case for headings** - at least for now, since it's what we use through the rest of the docs. There's research suggesting that sentence case (only first word of the heading starts with a capital) is actually superior for legibility and also reduces the cognitive overhead for documentation writers, since they don't have to try to remember whether to capitalize words like "and", "with", and "about".
+- **Don't use emojis (except in discussions).** Emojis are cute and friendly, but they can be a distraction in documentation and some emoji even convey different meanings in different cultures.
+
+## Iteration & Communication
+
+- **Excellence comes from iteration.** First drafts are always bad, but writing them is a vital part of the process. It's extremely difficult to avoid the slow progression of Bad -> OK -> Good -> Great -> Inspiring -> Transcendent.
+- **Only wait until something is "Good" before publishing.** The community will help you push it further down the chain.
+- **Try not to get defensive when receiving feedback.** Our writing can be very personal to us, but if we get upset with the people who help us make it better, they will either stop giving feedback or start limiting the kind of feedback they give.
+- **Proof-read your own work before showing it to others.** If you show someone work with a lot of spelling/grammar mistakes, you'll get feedback about spelling grammar/mistakes instead of more valuable notes about whether the writing is achieving your goals.
+- **When you ask people for feedback, tell reviewers what:**
+ - **you're trying to do**
+ - **your fears are**
+ - **balances you're trying to strike**
+- **When someone reports a problem, there is almost always a problem**, even if the solution they proposed isn't quite right. Keep asking follow-up questions to learn more.
+- People need to feel safe asking questions when contributing/reviewing content. Here's how you can do that:
+ - **Thank people for their contributions/reviews, even if you're feeling grumpy.** For example:
+ - "Great question!"
+ - "Thanks for taking the time to explain. 🙂"
+ - "This is actually intentional, but thanks for taking the time to contribute. 😊"
+ - **Listen to what people are saying and mirror if you're not sure you're understanding correctly.** This can help validate people's feelings and experiences, while also understanding if _you're_ understanding _them_ correctly.
+ - **Use a lot of positive and empathetic emojis.** It's always better to seem a little strange than mean or impatient.
+ - **Kindly communicate rules/boundaries.** If someone behaves in a way that's abusive/inappropriate, respond only with kindness and maturity, but also make it clear that this behavior is not acceptable and what will happen (according to the code of conduct) if they continue behaving poorly.
+
+### Tips, Callouts, Alerts, and Line Highlights
+
+We have some dedicated styles to denote something that's worth highlighting in a particular way. These are captured [on this page](https://v3.leafphp.dev/docs/doc-style-guide.html#alerts). **They are to be used sparingly.**
+
+There is a certain temptation to abuse these styles, as one can simply add a change inside a callout. However, this breaks up the flow of reading for the user, and thus, should only be used in special circumstances. Wherever possible, we should attempt to create a narrative and flow within the page to respect the readers cognitive load.
+
+Under no circumstances should 2 alerts be used next to one another, it's a sign that we're not able to explain context well enough.
+
+### Contributing
+
+We appreciate small, focused PRs. If you'd like to make an extremely large change, please communicate with team members prior to a pull request. Here's a [writeup that details why this is so critical](https://www.netlify.com/blog/2020/03/31/how-to-scope-down-prs/) for us to work well on this team. Please understand that though we always appreciate contributions, ultimately we have to prioritize what works best for the project as a whole.
+
+## Resources
+
+### Software
+
+- [Grammarly](https://www.grammarly.com/): Desktop app and browser extension for checking spelling and grammar (though grammar checking doesn't catch everything and occasionally shows a false positive).
+- [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker): An extension for VS Code to help you check spelling within markdown and code examples.
+
+### Books
+
+- [On Writing Well](https://www.amazon.com/Writing-Well-30th-Anniversary-Nonfiction-ebook/dp/B0090RVGW0) (see [popular quotes](https://www.goodreads.com/work/quotes/1139032-on-writing-well-the-classic-guide-to-writing-nonfiction))
+- [Bird by Bird](https://www.amazon.com/Bird-Some-Instructions-Writing-Life/dp/0385480016) (see [popular quotes](https://www.goodreads.com/work/quotes/841198-bird-by-bird-some-instructions-on-writing-and-life))
+- [Cognitive Load Theory](https://www.amazon.com/Cognitive-Explorations-Instructional-Performance-Technologies/dp/144198125X/)
+
+**Adapted from the Vue JS writing guide.**
diff --git a/apps/docs/src/community/faq.md b/apps/docs/src/community/faq.md
new file mode 100644
index 0000000..f90fc68
--- /dev/null
+++ b/apps/docs/src/community/faq.md
@@ -0,0 +1,65 @@
+# Frequently Asked Questions
+
+## Who maintains Leaf?
+
+Leaf is an independent, community-driven project. It was created by [Michael Darko](https://github.com/mychidarko) in 2019 as a personal side project. Today, Leaf is actively maintained by [a team of both full-time and volunteer members from all around the world](/community/team), where Michael serves as the project lead. You can find Leaf's history [here](/community/history).
+
+Leaf's development is primarily funded through sponsorships and personal contributions from Michael and friends. If you or your business benefit from Leaf, consider [sponsoring us](/support/) to support Leaf's development!
+
+## What license does Leaf use?
+
+Leaf is a free and open source project released under the [MIT License](https://opensource.org/licenses/MIT).
+
+## Is Leaf reliable?
+
+After going through 2 major versions over the past 2 years, Leaf is now a battle-tested framework suitable for just about any PHP project. It is also one of the most popular lightweight PHP micro-frameworks and even has it's variant - Leaf API being a very popular framework.
+
+Leaf is used by individuals for large and personal projects, as well as by companies all over the world.
+
+## Is Leaf fast?
+
+Leaf 3 is one of the most performant and lightweight PHP frameworks today. It is also 100% compatible with standard PHP libraries and servers, which means there's almost no need for configurations and manual optimizations.
+
+In sample benchmarks, Leaf even out-performs frameworks like Slim PHP which was considered the most lightweight PHP micro-framework with blazing speed.
+
+## Is Leaf lightweight?
+
+As mentioned above, Leaf 3 is one of the most lightweight PHP frameworks with a source of about 30kb. With Leaf 3's functional mode, a hello world application can be prepared in just 10 lines of code, including spaces and PHP initializers.
+
+## What's the difference between Leaf 2 and Leaf 3?
+
+Leaf 3 is the current, latest major version of Leaf. It contains new features that are not present in Leaf 2 (most notably functional mode and modules), and also contains a few breaking changes that makes it incompatible with Leaf 2. Despite the differences, the majority of Leaf APIs are shared between the two major versions, so almost all of your Leaf 2 knowledge will continue to work in Leaf 3.
+
+In general, Leaf 3 provides smaller bundle sizes, better performance, better scalability, and better IDE support. If you are starting a new project today, Leaf 3 is the recommended choice. The only reason for you to consider Leaf 2 as of now is if you feel Leaf 3 is too modular, of course this is solved if you use Leaf 3 with Leaf MVC or Leaf API.
+
+Since both Leaf 2 and 3 are wired to accept any PHP code, all external libraries usable in Leaf 2 will still work perfectly with Leaf 3. If you intend to migrate an existing Leaf 2 app to Leaf 3, consult the dedicated Leaf 3 Migration Guide.
+
+Leaf 2 will receive a final minor release (2.7) in 2022. This minor release will backport a selected subset of new features from Leaf 3. After that, Leaf 2 will enter maintenance mode: it will no longer ship new features, but will continue to receive critical bug fixes and security updates.
+
+## Does Leaf scale?
+
+Yes. Despite a common misconception that Leaf is only suitable for simple use cases, Leaf is perfectly capable of handling large scale applications:
+
+- [Skeleton](https://skeleton.leafphp.dev) provides a base setup which holds your hand through separation of concerns. It provides a partial MVC setup which allows for incremental adoption.
+
+ ::: warning Remember
+ This is not a framework.
+ :::
+
+- [Leaf API](https://api.leafphp.dev) is an MVC framework bootstrapped with Leaf at it's core. It uses the [MVC architecture](https://towardsdatascience.com/everything-you-need-to-know-about-mvc-architecture-3c827930b4c1) to ensure separation of concerns. Leaf API is specially crafted to aid rapid developement of powerful but scalable APIs.
+
+- [Leaf MVC](https://mvc.leafphp.dev) just like Leaf API is a framework built with leaf that relies on the MVC architecture. The main difference is leaf MVC comes with tools which aid in building fullstack web apps/APIs. This makes Leaf MVC more general purpose, compared to the one-track API machine: Leaf API.
+
+As you see above, all of these setups/frameworks are built using leaf as their core. Leaf can fit into any environment depending on how you use it.
+
+## How do I contribute to Leaf?
+
+We appreciate your interest! Please check out our [Contribution Guide](/community/contributing/).
+
+
+
+
+
+
diff --git a/apps/docs/src/community/history.md b/apps/docs/src/community/history.md
new file mode 100644
index 0000000..cb90457
--- /dev/null
+++ b/apps/docs/src/community/history.md
@@ -0,0 +1,23 @@
+---
+aside: none
+---
+
+# History
+
+Leaf was started in October 2019 as a simple frameworkless boilerplate for developing APIs. The idea was to create a powerful, but fairly simple boilerplate which could just be cloned and used as a starter for projects.
+
+![Leaf PHP boilerplate](https://user-images.githubusercontent.com/26604242/145728756-4b3908af-4cf1-4be6-8250-f39f4155489d.png)
+
+Along the way, leaf's features got bundled and shipped as a library which people started calling a micro-framework. It was later published on [product hunt](https://www.producthunt.com/posts/leaf-php) and got a whole lot of people interested in it. Leaf even earned one of the top products of the day spots ⚡️
+
+![image](https://user-images.githubusercontent.com/26604242/145728962-60179763-62c6-4f88-97b9-6d24bccc9f36.png)
+
+After this, leaf went on to introduce many amazing features like leaf db, leaf auth and more. But after a long run with version 1, we decided to completely re-write leaf in order to add a few more features like middleware on a core level.
+
+The first commit to v2 was on 4 Mar 2020. This was the first commit since the v1.5 release a couple of months earlier. The stable v2 was released only a month later on 22 Apr 2020, and brought in a whole lot of upgrades to leaf, leaf MVC, leaf API and the creation of Skeleton.
+
+A year down the line, leaf 2 was featured in a lot of top 10s and upcoming/lightweight framework reviews. It was adopted in both professional workscapes and by hobbyists all over the world. You could say leaf 2 was really doing well, but then it started getting features that a lot of developers were not using in their apps. And that's when the idea of modules was born.
+
+We could ship leaf's features as installable plugins which developers will manually handpick for their apps, and the community agreed with us. We started working towards this, and discovered "functional mode" along the way. We also rebranded leaf to match our new goal of giving developers the best PHP experience and improving our usability.
+
+Today, v3 is almost stable and we've published over 30 modules on packagist. v3 has proven to be lighter than just about every framework out there, with a source of about 30kb and even faster than frameworks like Slim PHP.
diff --git a/apps/docs/src/community/images/ben-hong.jpeg b/apps/docs/src/community/images/ben-hong.jpeg
new file mode 100644
index 0000000..5589197
Binary files /dev/null and b/apps/docs/src/community/images/ben-hong.jpeg differ
diff --git a/apps/docs/src/community/images/evan-you.jpeg b/apps/docs/src/community/images/evan-you.jpeg
new file mode 100644
index 0000000..388b889
Binary files /dev/null and b/apps/docs/src/community/images/evan-you.jpeg differ
diff --git a/apps/docs/src/community/join.md b/apps/docs/src/community/join.md
new file mode 100644
index 0000000..5a74d01
--- /dev/null
+++ b/apps/docs/src/community/join.md
@@ -0,0 +1,58 @@
+---
+title: "Our Community"
+---
+
+# Join the Leaf PHP Community!
+
+Leaf's community is growing incredibly fast and if you're reading this, there's a good chance you're ready to join it. So... welcome!
+
+Now we'll answer both what the community can do for you and what you can do for the community.
+
+## Resources
+
+### Code of Conduct
+
+Our [Code of Conduct](/coc/) is a guide to make it easier to enrich all of us and the technical communities in which we participate.
+
+### Get Support
+
+- [Forum](https://github.com/leafsphp/leaf/discussions/37): The best place to ask questions and get answers about Leaf and its ecosystem.
+- [GitHub](https://github.com/leafsphp/leaf): If you have a bug to report or feature to request, that's what the GitHub issues are for. We also welcome pull requests!
+- [Discord server](https://discord.gg/Pkrm9NJPE3): A place for Leaf devs to meet and chat in real time.
+
+
+
+## What You Can Do
+
+### Contribute Code
+
+As with any project, there are rules to contributing. To ensure that we can help you or accept your pull request as quickly as possible, please read [the contributing guide](/community/contributing/).
+
+After that, you'll be ready to contribute to Leaf's core repositories:
+
+- [Leaf](https://github.com/leafsphp/leaf): the core library
+- [Leaf MVC](https://github.com/leafsphp/leafMVC): MVC powered wrapper around Leaf PHP
+- [Leaf API](https://github.com/leafsphp/leafAPI): MVC powered wrapper specialized for building APIs.
+
+...as well as many smaller official [companion libraries](https://github.com/leafsphp).
+
+### Share (and Build) Your Experience
+
+Apart from answering questions and sharing resources in the forum and chat, there are a few other less obvious ways to share and expand what you know:
+
+- **Develop learning materials.** It's often said that the best way to learn is to teach. If there's something interesting you're doing with Leaf, strengthen your expertise by writing a blog post, developing a workshop, or even publishing a gist that you share on social media.
+- **Watch a repo you care about.** This will send you notifications whenever there's activity in that repository, giving you insider knowledge about ongoing discussions and upcoming features. It's a fantastic way to build expertise so that you're eventually able to help address issues and pull requests.
+
+### Help manage our Community
+
+With the growth of Leaf, we need more hands to moderate activities on our discord chat, keep up with our documentation, GitHub discussions, twitter and the like.
+
+If you want to help with any of these, kindly reach out on twitter [@leafphp](https://twitter.com/leafphp) or email [mychi@leafphp.dev](mailto:mychi@leafphp.dev)
+
+## Updates
+
+- [Youtube](https://twitter.com/leafphp): Get online streams, talks and tutorials on leaf and community packages.
+
+- [Twitter](https://twitter.com/leafphp): Get timely updates on everything that happens in the leaf PHP ecosystem.
+
+
diff --git a/apps/docs/src/community/releases.md b/apps/docs/src/community/releases.md
new file mode 100644
index 0000000..e43c677
--- /dev/null
+++ b/apps/docs/src/community/releases.md
@@ -0,0 +1,77 @@
+---
+outline: deep
+---
+
+
+
+# Releases
+
+
+The current latest stable version of Vue is {{ version }}.
+
+
+Checking latest version...
+
+
+A full changelog of past releases is available on [GitHub](https://github.com/vuejs/core/blob/main/CHANGELOG.md).
+
+## Release Cycle
+
+Vue does not have a fixed release cycle.
+
+- Patch releases are released as needed.
+
+- Minor releases always contain new features, with a typical time frame of 3~6 months in between. Minor releases always go through a beta pre-release phase.
+
+- Major releases will be announced ahead of time, and will go through an early discussion phase and alpha / beta pre-release phases.
+
+## Semantic Versioning Edge Cases
+
+Vue releases follow [Semantic Versioning](https://semver.org/) with a few edge cases.
+
+### TypeScript Definitions
+
+We may ship incompatible changes to TypeScript definitions between **minor** versions. This is because:
+
+1. Sometimes TypeScript itself ships incompatible changes between minor versions, and we may have to adjust types to support newer versions of TypeScript.
+
+2. Occasionally we may need to adopt features that are only available in a newer version of TypeScript, raising the minimum required version of TypeScript.
+
+If you are using TypeScript, you can use a semver range that locks the current minor and manually upgrade when a new minor version of Vue is released.
+
+### Compiled Code Compatibility with Older Runtime
+
+A newer **minor** version of Vue compiler may generate code that isn't compatible with the Vue runtime from an older minor version. For example, code generated by Vue 3.2 compiler may not be fully compatible if consumed by the runtime from Vue 3.1.
+
+This is only a concern for library authors, because in applications, the compiler version and the runtime version is always the same. A version mismatch can only happen if you ship pre-compiled Vue component code as a package, and a consumer uses it in a project using an older version of Vue. As a result, your package may need to explicit declare a minimum required minor version of Vue.
+
+## Pre Releases
+
+Minor releases typically go through a non-fixed number of beta releases. Major releases will go through an alpha phase and a beta phase.
+
+Pre-releases are meant for integration / stability testing, and for early adopters to provide feedback for unstable features. Do not use pre-releases in production. All pre-releases are considered unstable and may ship breaking changes in between, so always pin to exact versions when using pre-releases.
+
+## Deprecations
+
+We may periodically deprecate features that have new, better replacements in minor releases. Deprecated features will continue to work, and will be removed in the next major release after it entered deprecated status.
+
+## RFCs
+
+New features with substantial API surface and major changes to Vue will go through the **Request for Comments** (RFC) process. The RFC process is intended to provide a consistent and controlled path for new features to enter the framework, and give the users an opportunity to participate and offer feedback in the design process.
+
+The RFC process is conducted in the [vuejs/rfcs](https://github.com/vuejs/rfcs) repo on GitHub.
+
+## Experimental Features
+
+Some features are shipped and documented in a stable version of Vue, but marked as experimental. Experimental features are typically features that have an associated RFC discussion with most of the design problems resolved on paper, but still lacking feedback from real world usage.
+
+The goal of experimental features is to allow users to provide feedback for them by testing them in a production setting, without having to use an unstable version of Vue. Experimental features themselves are considered unstable, and should only be used in a controlled manner, with the expectation that the feature may change between any release types.
diff --git a/apps/docs/src/community/team.md b/apps/docs/src/community/team.md
new file mode 100644
index 0000000..dbde9c8
--- /dev/null
+++ b/apps/docs/src/community/team.md
@@ -0,0 +1,11 @@
+---
+page: true
+title: Meet the Team
+sidebar: false
+---
+
+
+
+
diff --git a/apps/docs/src/community/team/Member.ts b/apps/docs/src/community/team/Member.ts
new file mode 100644
index 0000000..9ad76d8
--- /dev/null
+++ b/apps/docs/src/community/team/Member.ts
@@ -0,0 +1,27 @@
+export interface Member {
+ name: string
+ avatarPic?: string
+ title: string
+ company?: string
+ companyLink?: string
+ projects: Link[]
+ location: string
+ languages: string[]
+ website?: Link
+ socials: Socials
+ sponsor?: boolean | string
+ reposPersonal?: string[]
+}
+
+export interface Link {
+ label: string
+ url: string
+}
+
+export interface Socials {
+ github: string
+ twitter?: string
+ codepen?: string
+ instagram?: string
+ linkedin?: string
+}
diff --git a/apps/docs/src/community/team/TeamHero.vue b/apps/docs/src/community/team/TeamHero.vue
new file mode 100644
index 0000000..0ad0f91
--- /dev/null
+++ b/apps/docs/src/community/team/TeamHero.vue
@@ -0,0 +1,75 @@
+
+
+
+ Meet the Team
+ The development of Leaf and its ecosystem is guided by a our
+ core team.
+
+
+
+
+
+ Core Team Members
+ Core team members are those who are actively involved in the
+ maintenance of one or more core projects. They have made significant
+ contributions to the Leaf ecosystem, with a long term commitment to the
+ success of the project and its users.
+
+
+
+
+
+
+
+Leaf CLI is a simple command line tool for creating and interacting with your Leaf applications. It gives you the options to create projects, install dependencies, run scripts, scaffold items and much more.
+
+## Installation
+
+
+
+You can install the leaf cli using composer. Composer is a dependency manager for PHP. You can follow the instructions on [getcomposer.org](https://getcomposer.org) to install composer on your system. From there, you should have access to the `composer` command from anywhere on your system.
+
+```bash
+composer --version
+```
+
+You should then be able to get the Leaf CLI up and and running on your system using composer:
+
+```bash
+composer global require leafs/cli
+```
+
+After that, you should have access to the `leaf` command from anywhere on your system.
+
+```bash
+leaf --version
+```
+
+## command not found: leaf
+
+If you get a `command not found: leaf` error, it means your composer bin is not in your system path.
+
+You need to make sure that Composer's system-wide vendor bin directory is in your system `$PATH` so the leaf executable can be located by your system. This directory exists in different locations based on your operating system; however, some common locations include:
+
+- Windows: `%USERPROFILE%\AppData\Roaming\Composer\vendor\bin`
+- macOS: `$HOME/.composer/vendor/bin`
+- GNU / Linux Distributions: `$HOME/.config/composer/vendor/bin` or `$HOME/.composer/vendor/bin`
+
+You could also find the composer's global installation path by running `composer global about` and looking up from the first line.
+
+## Adding composer bin to path
+
+Once you find your composer global installation path, you can add it to your path. **There are some examples below which you can copy and paste in your terminal.**
+
+Eg (Adding composer bin to path linux):
+
+```bash
+export PATH=$PATH:$HOME/.config/composer/vendor/bin
+```
+
+Eg (Adding composer bin to path mac):
+
+```bash
+export PATH=$PATH:$HOME/.composer/vendor/bin
+echo $PATH
+```
+
+::: tip NOTE
+To get leaf cli installed permanently, you will need to add your composer bin your `.bashrc` or `.zshrc` file on mac and linux.
+
+**zsh:**
+
+```bash
+echo 'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> ~/.zshrc
+source ~/.zshrc
+```
+
+**bash:**
+
+```bash
+echo 'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> ~/.bashrc
+source ~/.bashrc
+```
+
+:::
+
+## Creating a leaf app
+
+
+
+To start a new project, open up your terminal and move into a directory you want to generate your projects in. From there, you can use the `leaf create` command to set up a new Leaf app in that directory:
+
+```bash
+leaf create
+```
+
+This will prompt you to select a preset. Presets are quick ways to get your project up and running as quickly as possible. You can select a preset from the list of presets displayed to you:
+
+```bash
+? What kind of app do you want to create? [leaf]
+ [0] leaf
+ [1] leaf mvc
+ [2] leaf api
+ >
+```
+
+*You can select a number or type in the preset you prefer.*
+
+A leaf app will be generated based on the associated preset. As you can see, there are 3 presets:
+
+- **Leaf**: a bare Leaf project
+- **Leaf MVC**: a Leaf project with leaf mvc
+- **Leaf API**: a Leaf project with leaf api
+
+The Leaf CLI will automatically install the dependencies for the preset you selected and set up your project using Leaf 3. From there, you can `cd` into your project and start building.
+
+```bash
+cd
+leaf serve
+```
+
+### Custom installation New
+
+The Leaf CLI also comes with a custom installation option. This allows you to customize your project to your liking. You can select the features you want to add to your project and the Leaf CLI will set it up for you.
+
+```bash
+leaf create --custom
+```
+
+This will prompt you to select the features you want to add to your project. You can select the features you want to add to your project from the list of features displayed to you:
+
+```bash
+? What modules would you like to add? [none] eg: 1,2,7
+ [0] None
+ [1] Database
+ [2] Authentication
+ [3] Session support
+ [4] Cookie support
+ [5] CSRF protection
+ [6] CORS support
+ [7] Leaf Date
+ [8] Leaf Fetch
+ >
+```
+
+For Leaf MVC, you can also select things like the View engine you prefer to use:
+
+```bash
+? What view engine would you like to use? [Blade]
+ [0] Blade
+ [1] Bare UI
+ [2] React/Vue
+ >
+```
+
+Whether to add a bundler for your frontend assets:
+
+```bash
+? Do you want to add Vite to bundle your assets? [Yes]
+```
+
+And whether to add a testing framework:
+
+```bash
+? What testing framework would you like to use? [none]
+ [0] none
+ [1] pest
+ [2] phpunit
+ >
+```
+
+### GUI New
+
+These new options are quite a lot to take in, so we've also added a GUI to help you select the features you want to add to your project. The GUI allows you to select different structures and features you want to add to your project. The GUI can also install and setup frontend systems like React, Vue, Tailwind and more.
+
+You can get started with the following command:
+
+```bash
+leaf ui
+```
+
+
+
+### Quick presets
+
+Leaf CLI also provides a quicker way to initialize your project without having to go through the interactive installer. You can use the `--mvc`, `--api`, and `--basic` options to generate your project based on a specific presets. These generate the following:
+
+- `--basic`: a bare Leaf project
+- `--mvc`: a Leaf project with leaf mvc
+- `--api`: a Leaf project with leaf api
+
+```bash
+leaf create --mvc
+```
+
+### Adding Tests
+
+The Leaf CLI by default will generate your project without any testing framework. However, you will be prompted to add a testing framework if you select the `--custom` option.
+
+```bash
+leaf create --custom
+```
+
+You can still add a testing framework without using the `--custom` option by using either the `--pest` for Pest PHP tests:
+
+```bash
+leaf create --pest
+```
+
+Or the `--phpunit` option for PHPUnit tests:
+
+```bash
+leaf create --phpunit
+```
+
+### Using docker
+
+Leaf CLI also provides a way to generate your project with docker. You can use the `--docker` option to add all the necessary files to your project to run it with docker.
+
+```bash
+leaf create --docker
+```
+
+If you are using the `--custom` option, you will be asked if you want to add docker to your project. For the full docker guide, you can check out the [docker guide](/docs/introduction/docker).
+
+## Running your leaf apps
+
+After generating your leaf app, you can `cd` into the directory and spin up a local dev server using leaf cli's `serve` command.
+
+```bash
+cd backend-api
+leaf serve
+```
+
+You can also specify the port to run your leaf app on using the `--port` or `-p` options.
+
+```bash
+leaf serve -p 3000
+```
+
+### File watching
+
+In v2.1, you can also start the leaf server with hot module watching. This reloads your application anytime a change is made to your application code. To get started, simply start the leaf server with the `--watch` flag.
+
+```bash
+leaf serve --port 8000 --watch
+```
+
+### Dependency Management
+
+The serve command will also try to install dependencies for your project if it doesn't detect a `vendor` folder present in the current working directory.
+
+
+
+## Testing your Leaf apps
+
+Testing helps prevent bugs in your app which you may not catch until you publish your app to production. Leaf introduced a test runner which helps you initialize testing and run tests in your app without needing any config first. Alchemy has also been integrated into the Leaf CLI and so you can use it's functionality directly.
+
+### Setting up tests
+
+If you already have a project and want to setup tests, you can use the `test:setup`. It will automatically detect the testing framework you are using and setup tests for you. It also supports Pest PHP and PHPUnit, so you can use either of them using the `--phpunit` or `--pest` options.
+
+```bash
+# pest
+leaf test:setup --pest
+
+# phpunit
+leaf test:setup --phpunit
+```
+
+This will create a `tests` folder in your project and add example tests to it based on the testing framework you are using.
+
+### Running tests
+
+To run tests you've setup or created, you can use the `test` command.
+
+```bash
+leaf test
+```
+
+## Installing packages
+
+This cli tool also adds a feature to install leaf packages from composer.
+
+```bash
+leaf install leafs/ui
+```
+
+If you are installing a leaf module or package, you can leave out the `leafs/` part.
+
+```bash
+leaf install ui
+```
+
+You can also pass in a bunch of packages to install at once.
+
+```bash
+leaf install ui db illuminate/support
+```
+
+***Versioning***
+
+Leaf CLI also allows you to install a particular version of any package using `@`
+
+```bash
+leaf install ui@1.0 illuminate/support@9.0.2
+```
+
+## Interactive Shell
+
+You can also use the interactive shell to interact with your app.
+
+```bash
+$ leaf interact
+...
+>>> $user = new User;
+...
+>>> $user->name = "Mychi";
+...
+>>> $user->save();
+```
+
+## Updating leaf cli
+
+Leaf CLI keeps getting better with every release, and by default, it checks for updates every time you run a command. However, you can also manually update your leaf cli using the `update` command.
+
+```bash
+leaf update
+```
+
+If this doesn't work, or you want to hard reset the Leaf CLI to clear cache, you can re-install it via composer:
+
+```bash
+composer global remove leafs/cli
+composer global require leafs/cli
+```
+
+## View commands New
+
+Leaf CLI also allows you to create and interact with frontend setups using the `view` commands. You can scaffold frontend setups like React, Vue, templating engines, build tools, and more.
+
+### Scaffolding views
+
+Leaf CLI ships with a `view:install` command that allows you to setup React, Vue, and templating engines like Blade and BareUI. You can use the `--react`, `--vue`, `--blade`, and `--bareui` options to scaffold your frontend setup.
+
+```bash
+leaf view:install --react
+```
+
+You can also use the `--vite` and `--tailwind` options to scaffold Vite and Tailwind respectively.
+
+### Running frontend setups
+
+Since Leaf CLI is a backend tool, it doesn't come with a frontend server. However, you can use the `view:dev` command to run your frontend setup which may include in a dev server for your frontend.
+
+```bash
+leaf view:dev
+```
+
+### Building frontend setups
+
+You can also use the `view:build` command to build your frontend setup for production.
+
+```bash
+leaf view:build
+```
+
+## Running Scripts
+
+Leaf CLI also now allows you run scripts defined in your `composer.json` file. For example, if you have this in your composer.json:
+
+![image](https://user-images.githubusercontent.com/26604242/166419297-225b0b00-c979-4096-a23d-4f7858def8fb.png)
+
+You can run the test script like this:
+
+```bash
+leaf run test
+```
+
+## Usage Guide
+
+```bash
+ _ __ ___ _ ___
+| | ___ __ _ / _| / __| | |_ v2.8.3
+| |__/ -_) _` | _| | (__| |__ | |
+|____\___\__,_|_| \___|____|___|
+
+
+Usage:
+ command [options] [arguments]
+
+Options:
+ -h, --help Display help for the given command. When no command is given display help for the list command
+ -q, --quiet Do not output any message
+ -V, --version Display this application version
+ --ansi|--no-ansi Force (or disable --no-ansi) ANSI output
+ -n, --no-interaction Do not ask any interactive question
+ -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
+
+Available commands:
+ completion Dump the shell completion script
+ create [init|new] Create a new Leaf PHP project
+ deploy [publish] Deploy your leaf project
+ help Display help for a command
+ install Add a new package to your leaf app
+ interact Interact with your application
+ list List commands
+ run Run a script in your composer.json
+ serve Run your Leaf app
+ test Test your leaf application through leaf alchemy
+ uninstall Uninstall a package
+ update Update leaf cli to the latest version
+ test
+ test:setup Add tests to your application
+ view
+ view:build Run your frontend dev server
+ view:dev Run your frontend dev server
+ view:install Run a script in your composer.json
+```
+
+This is the full list of commands available with Leaf CLI 2. A new update command has been added to allow you seamlessly update leaf CLI without having to run a bunch of commands. You don't even need to run this manually since leaf cli will automatically check for updates and upgrade to the latest stable release.
diff --git a/apps/docs/src/docs/config/index.md b/apps/docs/src/docs/config/index.md
new file mode 100755
index 0000000..396dd8d
--- /dev/null
+++ b/apps/docs/src/docs/config/index.md
@@ -0,0 +1,148 @@
+# Overview
+
+
+
+
+
+Unlike other frameworks, Leaf requires no configuration out of the box. However, Leaf provides options for those who want to customize the framework to their needs.
+
+## Applying Config
+
+There are 32 main ways to apply config to your Leaf application. Although they achieve the same result, each method has its own advantages and disadvantages. Let's take a look:
+
+
+
+- **Passing config during instantiation**
+
+ To define settings upon instantiation, pass an associative array into the Leaf constructor. The array keys are the setting names and the array values are the setting values. This is the most performant way to define settings for Leaf, and we'll recommend this if you're using class mode.
+
+ ```php
+ $app = new Leaf\App([
+ 'debug' => true
+ ]);
+ ```
+
+- **Using the `config()` method**
+
+ This method is the most common way to apply config to your Leaf application. It's also the most flexible way to apply config. You can apply config at any point in your application, and you can apply multiple config at once. *Note that the config will only be applied to code that comes after the config method.*
+
+ ```php
+ $app = new Leaf\App;
+ $app->config([
+ 'debug' => true,
+ 'views.path' => '../views'
+ ]);
+ ```
+
+
+
+
+- **Using the `config()` method**
+
+ The `config()` method is the recommended way to apply config to your Leaf application. It allows you to set and get config values at any point in your application, and you can apply multiple config at once. *Note that the config will only be applied to code that comes after the config method.*
+
+ ```php
+ app()->config([
+ 'debug' => true,
+ 'views.path' => '../views'
+ ]);
+ ```
+
+
+
+- **Using the `Leaf\Config` class**
+
+ The Config class is the central point for all of Leaf's config. It allows you to set and get config from anywhere in your app. However, it is best to set config before initializing Leaf.
+
+ ```php
+ Leaf\Config::set([
+ 'views.path' => 'views',
+ 'views.cachePath' => 'views/cache'
+ ]);
+
+ // your leaf app after this
+ ```
+
+## Nested Config
+
+Leaf allows you to nest config into groups. This means that you can group config into arrays. This is especially useful when you're scoping features based on some configuration.
+
+For example, you can group all your server config into a `server` array:
+
+
diff --git a/apps/docs/src/docs/config/nsm.md b/apps/docs/src/docs/config/nsm.md
new file mode 100755
index 0000000..baa1e91
--- /dev/null
+++ b/apps/docs/src/docs/config/nsm.md
@@ -0,0 +1,99 @@
+# Application Environment
+
+
+
+
+
+When building an application, it is helpful to distinguish between what is "running locally" versus what is "running in production". For example, you may have a different database running locally than you do on your production server.
+
+To make this a breeze, Leaf provides robust support for environment based configuration, allowing you to conveniently handle configuration values for different environments.
+
+## Environment variables
+
+In a sense of your applications, environment variables are dynamic values that affect the way your applications behave. They are part of the environment in which a process runs. For example, your app can use the value of the TEMP environment variable to discover a suitable location to store temporary files. Environment variables are easy to change between environments, such as development, staging, and production.
+
+
+
+## Loading env variables
+
+Leaf doesn't come with an env loader out of the box, but you can add one yourself. Env loaders allow you to load environment variables from a `.env` file into PHP's `$_ENV` and `$_SERVER` globals. You can then access these variables using the `_env()` helper function that Leaf provides. Here's a list of some of the most popular env loaders:
+
+- [vlucas/phpdotenv](https://github.com/vlucas/phpdotenv)
+- [symfony/dotenv](https://github.com/symfony/dotenv)
+
+## Using env variables
+
+After loading your env variables, you can access them using the `_env()` helper function. The `_env()` function takes in a key and a default value. If the key is found, the value is returned, otherwise the default value is returned.
+
+```php
+$serverEnvironment = _env('APP_ENV', 'development');
+```
+
+## Application Modes
+
+Using the concept of environments like `development`, `testing`, and `production` is a common way to prepare an application to behave correctly in each environment. Leaf takes this concept one step further and introduces the concept of application modes. Application modes allow you to configure your application for a specific purpose.
+
+For example, you may want to enable debugging in “development” mode but not in “production” mode. The examples below demonstrate how to configure Leaf differently for a given mode.
+
+### Setting your application mode
+
+Leaf will automatically set the application mode based on the value of the `APP_ENV` environment variable. If the `APP_ENV` environment variable is not set, Leaf will set the application mode to `development`. This is because Leaf assumes you are developing your application locally when no environment is set.
+
+You can also set the application mode manually using the `mode` setting in your application settings.
+
+
+
+### Using your application mode
+
+The Leaf instance provides a `script()` method that allows you to register a callable that will be invoked when the application mode matches the given mode. The `script()` method accepts two arguments: the mode and a callable.
+
+
+
+The above example will enable logging and disable debugging when the application mode is set to `production`.
diff --git a/apps/docs/src/docs/config/settings.md b/apps/docs/src/docs/config/settings.md
new file mode 100755
index 0000000..37bba91
--- /dev/null
+++ b/apps/docs/src/docs/config/settings.md
@@ -0,0 +1,128 @@
+# App Settings
+
+
+
+Leaf has lots of configurations which can be used to determine the way Leaf behaves in your application. You can check the [config page](/docs/config/) for more information on how to set and get configs. This page will show you all the configs available in Leaf. Note that we will only cover the user-facing configs, configs that are used internally by Leaf will not be covered here.
+
+## app.down
+
+This configuration tells Leaf whether to place your app in a maintenance like state. By default, this is set to `false`, but you can set it to `true` to place your app in a maintenance like state.
+
+```php
+'app.down' => true
+```
+
+When your app is in a maintenance like state, Leaf will automatically load the `down` screen. You can customize this screen using Leaf's `setDown` method.
+
+```php
+$app->setDown(function () {
+ echo 'Custom Down Handler!';
+});
+```
+
+**You can read more about the down screen [here](/docs/routing/errors#application-down).**
+
+## debug
+
+By default, Leaf will display all errors and warnings you encounter while developing your app. However, when you're ready to deploy your app, you should turn off debugging to prevent users from seeing errors. You can do this by setting the `debug` config to `false`.
+
+```php
+'debug' => false
+```
+
+Note that when you set `debug` to `false`, Leaf will automatically turn off error reporting and display a custom error page to users. You can customize this page using Leaf's `setError` method.
+
+```php
+$app->setError(function () {
+ echo "Custom Error Handler!";
+});
+```
+
+## Logging
+
+Leaf has a built-in logger that can be used to log errors and other messages. You can check the [logging page](/docs/tooling/logging) for more information on how to use Leaf's logger. This page will show you all the configs available for Leaf's logger.
+
+To get started, make sure you have the [logger module](/docs/tooling/logging) installed. Once installed, you should have access to a `logger()` method on the Leaf instance. You can use this method to access Leaf's logger if you want to manually log messages.
+
+```php
+$app->logger()->info('Hello World!');
+```
+
+Below are all the configs available for Leaf's logger:
+
+### log.enabled
+
+This enables or disables Leaf’s logger. You need to set this to `true` to enable logging.
+
+```php
+'log.enabled' => true
+```
+
+Note that if `log.enabled` is set to `false`. Leaf will skip initializing anything related to logs, as such, you won't have access to `$app->logger()`, `$app->log` or `$app->logWriter`.
+
+### log.dir
+
+This tells leaf which directory to save and look for logs.
+
+```php
+'log.dir' => __DIR__ . '/logs/'
+```
+
+### log.file
+
+This setting tells leaf which file to write logs to.
+
+```php
+'log.file' => 'crashes.log'
+```
+
+*By default, Leaf will write logs to a file named `log.txt`.*
+
+### log.level
+
+Leaf has these log levels:
+
+- \Leaf\Log::EMERGENCY
+- \Leaf\Log::ALERT
+- \Leaf\Log::CRITICAL
+- \Leaf\Log::ERROR
+- \Leaf\Log::WARN
+- \Leaf\Log::NOTICE
+- \Leaf\Log::INFO
+- \Leaf\Log::DEBUG
+
+The `log.level` application setting determines which logged messages will be honored and which will be ignored. For example, if the `log.level` setting is `\Leaf\Log::INFO`, debug messages will be ignored while info, warn, error, and fatal messages will be logged.
+
+### log.open
+
+This option takes in a boolean and determines whether Leaf should create the specified log file if it doesn't exist.
+
+### log.writer
+
+Use a custom log writer to direct logged messages to the appropriate output destination. By default, Leaf’s logger will write logged messages to `STDERR`. If you use a custom log writer, it must implement this interface:
+
+```php
+public write(mixed $message, int $level);
+```
+
+The `write()` method is responsible for sending the logged message (not necessarily a string) to the appropriate output destination (e.g. a text file, a database, or a remote web service). The `$level` argument is one of the log levels listed above.
+
+```php
+'log.writer' => new \My\LogWriter()
+```
+
+## views.path
+
+The relative or absolute path to the filesystem directory that contains your Leaf application’s view files.
+
+```php
+'views.path' => './views'
+```
+
+## views.cachePath
+
+When using a view engine, this config tells Leaf where to store cached views.
+
+```php
+'views.cachePath' => './views/cache'
+```
diff --git a/apps/docs/src/docs/introduction/docker.md b/apps/docs/src/docs/introduction/docker.md
new file mode 100644
index 0000000..5440efc
--- /dev/null
+++ b/apps/docs/src/docs/introduction/docker.md
@@ -0,0 +1,130 @@
+# Using Docker
+
+
+
+Docker allows developers to package their applications and dependencies into lightweight and portable containers, which can be easily deployed and run on any environment, making it easier to develop and deploy applications consistently. This means you don't have to worry about configuring or setting up complicated development tools such as web servers and databases on your local machine.
+
+
+
+This guide will walk you through how to set up your Leaf application using Docker from scratch. To get started, you need to install [Docker Desktop](https://www.docker.com/products/docker-desktop/). After this, you can either use the Leaf CLI or manually create your application.
+
+## Using the Leaf CLI
+
+The Leaf CLI provides a simple way to get started with Docker in your Leaf applications. To create a new Dockerized Leaf app using the Leaf CLI, you need to add the `--docker` option to the `leaf create` command:
+
+```bash
+leaf create my-app --docker
+```
+
+This will setup a new Leaf application with Docker support. Although your app is dockerized, Leaf CLI still allows you to use the `serve` command to start your application. This command will automatically start your application using Docker instead of the built-in server.
+
+```bash
+leaf serve
+```
+
+### Docker Files
+
+You will notice a bunch of docker related files in your project. These are added by the Leaf CLI since you used the `--docker` option. You can always customize these files to suit your specific needs.
+
+```bash
+├── docker
+│ ├── 000-default.conf
+│ ├── Dockerfile
+│ └── php.ini
+├── docker-compose.yml
+```
+
+- `000-default.conf` is the Apache configuration file that will be used by the Docker container.
+- `Dockerfile` is the Dockerfile that will be used to build the Docker image.
+- `php.ini` is the PHP configuration file that will be used by the Docker container.
+- `docker-compose.yml` is the Docker Compose file that will be used to start the Docker container.
+
+## Adding to existing projects
+
+If you already have an existing Leaf application and you want to add Docker support to it, you will need to do so manually. We have provided a sample below that you can use as a reference.
+
+**Dockerfile:**
+
+```dockerfile
+FROM php:8.1-apache
+
+COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
+
+RUN a2enmod rewrite
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ libzip-dev \
+ wget \
+ git \
+ unzip
+
+RUN docker-php-ext-install zip pdo pdo_mysql
+
+RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
+
+RUN composer global require leafs/cli
+
+RUN ln -s /root/.composer/vendor/bin/leaf /usr/local/bin/leaf
+
+# If you have a custom PHP ini file you can uncomment this line
+# COPY ./php.ini /usr/local/etc/php/php.ini
+
+RUN apt-get purge -y g++ \
+ && apt-get autoremove -y \
+ && rm -rf /var/lib/apt/lists/* \
+ && rm -rf /tmp/*
+
+WORKDIR /var/www
+
+RUN chown -R www-data:www-data /var/www
+
+CMD ["apache2-foreground"]
+```
+
+**docker-compose.yml:**
+
+```yaml
+version: '3.1'
+services:
+ application:
+ build: ./docker
+ image: leafphp/docker
+ ports:
+ - '8080:80'
+ volumes:
+ - .:/var/www
+```
+
+**000-default.conf:**
+
+```apacheconf
+
+ ServerAdmin webmaster@localhost
+ DocumentRoot /var/www
+
+
+ Options Indexes FollowSymLinks
+ AllowOverride All
+ Require all granted
+
+
+```
+
+After adding these files, you can start your application using Docker by running the following command:
+
+```bash
+leaf serve
+```
+
+Or with the docker compose command:
+
+```bash
+docker compose up
+```
diff --git a/apps/docs/src/docs/introduction/features.md b/apps/docs/src/docs/introduction/features.md
new file mode 100644
index 0000000..6b052b2
--- /dev/null
+++ b/apps/docs/src/docs/introduction/features.md
@@ -0,0 +1,39 @@
+---
+aside: none
+---
+
+# Features
+
+Leaf has a ton of features which help provide the best developer experience without sacrificing any power. Leaf 3 makes these features even more usable. These are some of leaf's features:
+
+## Low barrier to entry
+
+Leaf is the easiest framework to learn with PHP newbies building powerful leaf apps in a few minutes of reading the docs/watching out tutorial videos. All you truly need to get started with Leaf is basic PHP knowledge and optional but recommended knowledge on some backend concepts like JWT auth and more.
+
+## Lightweight
+
+Leaf 2 was lightweight and fast enough to be considered one of the most lightweight but powerful frameworks around, and Leaf 3 makes leaf 2 look like a joke. Leaf 3 can now be considered the most lightweight PHP framework with a source of about 30kb and allows you to build full apps and APIs which end up less than 20mb including user dependencies (leaf api). This is a big haul compared to other frameworks which require dependencies and tons of files which end up more than 100mb.
+
+![image](https://user-images.githubusercontent.com/26604242/146754044-4c71c4ec-7b37-4c85-9c8b-56e8c2b54831.png)
+
+> a comparism with slim - slim (left) - leaf (right)
+
+## Enables high developer productivity
+
+A whole lot of research and testing has been done to build amazing features which allow developers to focus on only what they need: their apps. Leaf 3 has put tons of strategies together to create the best developer experience known to PHP. From things like removing class initializers and creating global functions which allow you call classes from anywhere in your application, modules and other amazing leaf features.
+
+## Powered by [modules](/modules/)
+
+Leaf 3 and its ecosystem are heavily powered by modules, which are simply pieces of leaf's functionality shipped into independently installable libraries. Modules help make leaf even more lightweight and help developers only deal with features which they need in their applications. This means that you only install what you need.
+
+## Easy to use features
+
+As mentioned above, a lot of research has gone into the developer experience for leaf 3 and one aspect was to make our existing features more performant and easier to use. We employed various strategies like modeling some features after popular libraries in other languages and frameworks. For instance, the API for leaf cors is almost an exact replica of the expressjs cors middleware.
+
+## Library/Framework compatibility
+
+Since the beginning of Leaf, we've set out to create code which could easily be integrated with other libraries and frameworks. No matter how powerful leaf is, we try to base of everything we do on simple concepts as opposed to other frameworks which need things like providers in order to access framework features in libraries.
+
+## Scalability
+
+One of the most beautiful things about leaf is that, no matter what package you're using with leaf, if it works in development, it will definitely work in production with near zero config, unless you want some special features. Leaf provides a core and other frameworks/libraries that build around leaf. This makes leaf appropriate for almost any project no matter it's size.
diff --git a/apps/docs/src/docs/introduction/first-app.md b/apps/docs/src/docs/introduction/first-app.md
new file mode 100644
index 0000000..6c48606
--- /dev/null
+++ b/apps/docs/src/docs/introduction/first-app.md
@@ -0,0 +1,469 @@
+# Your first app
+
+::: tip Pre-requisites
+
+- Basic PHP knowledge
+
+:::
+
+This is an interactive tutorial to help you get started with leaf, right from installation to building your first leaf 3 app. This tutorial will require basic PHP knowledge with an optional knowledge on APIs.
+
+## Getting started
+
+::: tip Leaf CLI
+We recommend using the [Leaf CLI](/docs/cli/) for creating and managing your Leaf projects.
+:::
+
+To get started, we will need to generate a leaf application. We can do this simply with Leaf CLI:
+
+```bash
+leaf create --basic
+```
+
+
+
+Or with composer:
+
+```bash
+composer require leafs/leaf
+```
+
+After this, you will need to create an `index.php` file. *This is already done for you if you used Leaf CLI.*
+
+## Your app starter
+
+Now that leaf is installed, you will need a file which will serve as the root of your project. Leaf uses a single root file, to which all routes are sent to. Leaf then takes the route and calls the related handler for it. You can read more in the [routing docs](/docs/routing/).
+
+Your starter file will import leaf and hold your routes.
+
+```php
+
+
+Let's define a dummy route.
+
+```php{3-5}
+$app = new Leaf\App();
+
+$app->get('/', function () {
+ echo 'something';
+});
+
+// don't forget to call `run`
+$app->run();
+```
+
+
+
+
+
+We would normally need to initialize leaf, however, with with the introduction of functional mode, we don't need to do this. We can go straight to building our app.
+
+Let's define a dummy route.
+
+```php{1-3}
+app()->get('/', function () {
+ echo 'something';
+});
+
+// don't forget to call `run`
+app()->run();
+```
+
+
+
+Defining a route is that simple with leaf. In this case, we just defined the `GET /` route. We can simply run this with `leaf serve` if you use the leaf CLI or `php -S localhost:[PORT]`.
+
+## Our app
+
+Now that we've gotten our hello world setup complete, we can get started with our application. We will build a simple note taking app which will allow us create and fetch notes from a database.
+
+## Modules
+
+Modules are pieces of leaf's functionality which are served as installable plugins. They were created in an attempt to stop leaf from bloating like other frameworks. This means that you can have only what you need in your app, and always extend Leaf's power on demand.
+
+As you will see, modules are installed using the Leaf CLI or composer. For this app, we'll be using the `db` module to access our database.
+
+## Getting our notes
+
+### Creating our route
+
+To get started with this step, we need to create a GET route which will return all the notes in our database. Since we already know how to create routes like the one above, this step is pretty simple.
+
+
+
+```php
+$app->get('/notes', function () {
+ // fetch all notes from the database
+ // output notes as JSON
+ echo 'all notes';
+});
+```
+
+
+
+
+
+```php
+app()->get('/notes', function () {
+ // fetch all notes from the database
+ // output notes as JSON
+ echo 'all notes';
+});
+```
+
+
+
+### Fetching notes
+
+As mentioned above, we will use the db module to access our database. Leaf DB has made database operations really simple and accessible to everyone. You don't even need knowledge on SQL to use Leaf DB.
+
+#### Installing leaf db
+
+To install the db module, we can use the Leaf CLI.
+
+```bash
+leaf install db
+```
+
+You can also use composer:
+
+```bash
+composer require leafs/db
+```
+
+#### Connecting to our db
+
+From there, we can head back inside our app and connect to our database.
+
+
+
+We can place this before before our routes so we can use the `$db` variable everywhere.
+
+#### Using the db module
+
+
Back in our route, we can pass the `$db` variable into scope and get started with it. You can check the [db module docs](/modules/db/) for more info.
+
+What we want to do here is retrieve all the data from our notes table, we can do this simply using `select`. This is a method provided by leaf db which allows us run the SQL `Select` command.
+
+
+
+```php
+$app = new Leaf\App;
+$db = new Leaf\Db;
+
+$db->connect('127.0.0.1', 'dbname', 'username', 'password');
+
+// pass db into the callback using `use`
+$app->get('/notes', function () use($db) {
+ // fetch all notes from the database
+ $notes = $db->select('notes')->all();
+
+ // output notes as JSON
+ echo 'all notes';
+});
+```
+
+
+
+
+
+```php
+db()->connect('127.0.0.1', 'dbname', 'username', 'password');
+
+app()->get('/notes', function () {
+ // fetch all notes from the database
+ $notes = db()->select('notes')->all();
+
+ // output notes as JSON
+ echo 'all notes';
+});
+```
+
+
+
+Now that we've been able to retrieve our data from the database, let's see how we can output this data.
+
+### The response object
+
+The response object is leaf's library for handling the way data flows out of your application. It has a very simple and easy to use interface , and with functional mode, it can be used from anywhere in your app without initilaizing it.
+
+In the lines above, we retrieved our data from the database. Now all that's left is to output this data as JSON. We can do this simply by calling `json` on the response object.
+
+
+
+This will output JSON
+
+```json
+{
+ "status": "success",
+ "data": [...]
+}
+```
+
+## Saving our notes
+
+We need to create another route to handle adding new notes. In this case, we will create a POST route, meaning you will need to create a POST request to access it. This can be done using an HTTP client of some sort.
+
+This new route will take some data into our application and then select only what we need to be saved in the database, and finally return a message.
+
+
+
+```php
+$app->post('/notes/new', function () use($db) {
+ // get data from request
+ // save items
+ // return success message
+});
+```
+
+
+
+
+```php
+app()->post('/notes/new', function () {
+ // get data from request
+ // save items
+ // return success message
+});
+```
+
+
+
+### The request object
+
+Just as we saw with the response object, Leaf also provides a request object which allows us to quickly and securely get data which flows into our application.
+
+
+
+This line will get data with the key `item` passed into the app from a form, url or any other data and save it in the `$item` variable. In this case, our app will accept `title`, `body` and `date` which we will save in the database.
+
+To do this, we can retrieve them one by one as we did above, but leaf provides an easier way.
+
+
+
+With this, all other data passed in our app will be ignored, but will still be available for use.
+
+### Saving data in the db
+
+To save the data in the database, we will use leaf db as we did above. This time, the `insert` method instead.
+
+
+
+Building is this simple with leaf, as you can see, we've built a note taking app in less than 30 lines of code.
diff --git a/apps/docs/src/docs/introduction/index.md b/apps/docs/src/docs/introduction/index.md
new file mode 100644
index 0000000..2bcc10b
--- /dev/null
+++ b/apps/docs/src/docs/introduction/index.md
@@ -0,0 +1,156 @@
+# Introduction
+
+
+
+
+
+Leaf is a slim and lightweight PHP framework focused on developer experience, usability, and high-performance code. It is a modern PHP framework built to be simple and elegant, yet extremely powerful.
+
+Here's an example:
+
+```php
+get('/', function () {
+ response()->json(['message' => 'Hello World!']);
+});
+
+app()->run();
+```
+
+## Features
+
+Leaf has a ton of features that make it a great choice for building your next project. Here are some of the features that make Leaf stand out:
+
+- Lightweight
+- Super performant
+- Easy to learn
+- Easy to use
+
+For an in-depth look at Leaf's features, check out the [features page](/docs/introduction/why.html).
+
+## Getting Started
+
+The official guide assumes **basic** level knowledge of PHP.
+
+::: tip Down to the basics
+
+
+😵💫 Don't know PHP?
+If you are not familiar with PHP, we recommend that you check out the W3Schools PHP Tutorial before continuing or use the video below. This is because you will basically be writing PHP code when using Leaf (or any other framework).
+
+
+
+
+
+What's a PHP Framework?
+If you are not familiar with the concept of PHP frameworks, you can check out the video below by Kinsta.
+
+
+
+
+:::
+
+### Installation
+
+
+
+To quickly get started with Leaf, check out our [installation guide](/docs/introduction/installation.html). This gives you an in-depth explanation of how to set up leaf using various methods.
+
+## Classes vs Functions
+
+Leaf supports two different ways of writing your code:
+
+- Using functional mode which you saw above
+- Using class mode which is building your app using Leaf's classes
+
+### Class Mode
+
+If you are familiar with PHP frameworks, you've probably spent a lot of time writing code using classes. Leaf comes with a ton of powerful classes that you can use to build your app. Let's take a look at the code above in class mode.
+
+```php
+get('/', function () use ($app) {
+ $app->response()->json(['message' => 'Hello World!']);
+});
+
+$app->run();
+```
+
+### Functional Mode
+
+Classes can become annoying and very repetitive, especially because of namespaces. You also sometimes need to worry about variable scoping and re-initializing classes.
+
+For these and many more reasons, Leaf allows you to build your apps entirely using functions. Let's take a look at the code above in functional mode.
+
+```php
+get('/', function () {
+ response()->json(['message' => 'Hello World!']);
+});
+
+app()->run();
+```
+
+Besides the benefits functional mode gives you on a surface level, it also does a lot of work behind the scenes to make your code more efficient and faster.
+
+## Modes in the docs
+
+The docs are written in both functional and class mode. You can switch between the two modes using the switcher at the top of the page.
+
+![Switcher](https://user-images.githubusercontent.com/26604242/178108346-c9c22a19-6a82-4786-ac3e-00cbfe69cba8.png)
+
+## Leaf modules
+
+Modules are pieces of Leaf's functionality that are available as separate packages. These packages can be installed and used with Leaf to extend its functionality. Most modules are framework agnostic and can be used with other frameworks and libraries, but they are built to work best with Leaf.
+
+You can check out the [modules page](/modules/) to see a list of all the modules available for Leaf.
+
+## Ready for More?
+
+We've briefly introduced the most basic features of Leaf 3 - the rest of this guide will cover them and other advanced features in much finer detail, so make sure to read through it!
+
+## Next Steps
+
+If you are new to Leaf, we strongly recommend reading this page before moving on to the rest of the documentation. It will provide you with a solid foundation on which to build your Leaf knowledge. If you are already familiar with Leaf, feel free to check out the rest of the documentation.
+
+
diff --git a/apps/docs/src/docs/introduction/installation.md b/apps/docs/src/docs/introduction/installation.md
new file mode 100644
index 0000000..efb9509
--- /dev/null
+++ b/apps/docs/src/docs/introduction/installation.md
@@ -0,0 +1,199 @@
+# Installation
+
+
+
+
+
+Leaf 3 is built by design to be incrementally adoptable. This means that it can be integrated into a project multiple ways depending on the requirements.
+
+There are four primary ways of adding Leaf PHP to a project:
+
+1. [Use the Leaf CLI to scaffold a project [RECOMMENDED]](#leaf-cli).
+2. [Download leaf through composer](#composer)
+3. [Scaffold a Leaf MVC or Leaf API project](#mvc-setup)
+
+## Technical Requirements
+
+Before creating your first Leaf application you must:
+
+- Install PHP 7.4 or higher and these PHP extensions (which are installed and enabled by default in most PHP installations): json, zip;
+- [Install Composer](https://getcomposer.org/download/), which is used to install PHP packages.
+- Optionally, you can also install [Leaf CLI](/docs/cli/). This provides all the tools you need to create and manage your Leaf application locally. This is optional but highly recommended.
+
+
+Not sure where to start?
+
+- Laravel released an amazing tool called [Laravel Herd](https://herd.laravel.com/) that provides a quick and easy way to set up a local PHP development environment for Mac. It's a great way to get started with PHP and Leaf.
+
+- On Windows and Mac, you can use [Xampp](https://www.apachefriends.org/), which is a free and open-source cross-platform web server solution stack package developed by Apache Friends, consisting mainly of the Apache HTTP Server, MariaDB database, and interpreters for scripts written in the PHP and Perl programming languages.
+
+
+
+## Leaf CLI
+
+
+
+Leaf provides an [official CLI](https://github.com/leafsphp/cli) for quickly creating and managing your Leaf applications. It takes just a few seconds to get up and running with your leaf app. See [the Leaf CLI docs](/docs/cli/) for more details.
+
+Using the CLI, you can quickly scaffold a new Leaf 3 project with:
+
+```bash
+leaf create
+```
+
+Besides the core of the framework, Leaf also ships with a ton of installable functionality. We call these independent libraries modules. You can install modules using the `install` command:
+
+```bash
+leaf install
+```
+
+The CLI also allows you to completely customize the installation you wish to create. You can choose different features like database, authentication, etc. This is done using the `--custom` flag:
+
+```bash
+leaf create --custom
+```
+
+You can then run your app using the `serve` command:
+
+```bash
+leaf serve
+```
+
+## Composer
+
+
+
+Leaf also allows a more traditional approach to installation. You can install leaf through composer. You can use this method if you don't want to use the leaf cli or if you want to use leaf as a dependency in your project. The disadvantage of this method is that you don't get a quick-start setup like you do with the leaf cli.
+
+```bash
+composer require leafs/leaf
+```
+
+After insalling Leaf, you need to create your index.php file which will be the entry point to your application.
+
+
+
+When hosting your application on a webserver, all requests coming into your app must be routed through Leaf. It is really simple to do, and all needed instructions can be found @ [URL rewriting](/docs/introduction/url-rewriting.html).
+
+## MVC Setup
+
+Although Leaf allows you to select exactly what you want to install, some applications go beyond the basic setup. Leaf MVC is a full but ridiculously light-weight MVC framework that allows you to build complex applications with Leaf. It comes with a lot of features like authentication, database, http related functionality and a powerful CLI. To get started with Leaf's MVC setup, you can check out the [MVC docs](/docs/mvc/).
+
+## Hello world example
+
+Below is a "hello world" example which takes you through the core of Leaf. Other parts of the docs cover deeper examples. You can also refer to our [codelab experiments](/codelabs/) for real world examples and use-cases.
+
+A base Leaf app that outputs hello world in your browser looks like this:
+
+
+
+You might have noticed that we used `echo` to output our data. Using `echo` is not a bad thing, but it can be confusing when you're trying to output data of a different type. For example, if we wanted to output JSON data, we would have to use `echo json_encode($data)`. This can be confusing because we're not sure if the content type is set to JSON or not.
+
+To simplify this, Leaf comes with a `Response` object that automatically handles content the right way for us. Let's look at an example below.
+
+
diff --git a/apps/docs/src/docs/introduction/url-rewriting.md b/apps/docs/src/docs/introduction/url-rewriting.md
new file mode 100755
index 0000000..61012de
--- /dev/null
+++ b/apps/docs/src/docs/introduction/url-rewriting.md
@@ -0,0 +1,43 @@
+
+
+# URL Rewriting
+
+
+
+Basically, we're trying to push all the requests made to the server to a single root file, so a request made to `/home.php` will be directed to the root file of our choice....usually `index.php`.
+
+This complex sounding feature can be achieved by adding a web server configuration depending on your server of choice.
+
+
+
+## Apache - .htaccess
+
+This is a basic example of an htaccess file. It basically re-routes all requests to our index.php file.
+
+```htaccess
+RewriteEngine on
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteRule . index.php [L]
+```
+
+Save as `.htaccess` in your the same directory as your "root file"
+
+## Nginx - nginx.conf
+
+A basic example with nginx web server
+
+```nginx
+try_files $uri /index.php;
+```
+
+You can read below for an overview of url rewriting.
+
+- [Intro to URL rewriting](https://www.smashingmagazine.com/2011/11/introduction-to-url-rewriting/)
+- [.htaccess and nginx.conf variations](https://gist.github.com/bramus/5332525)
diff --git a/apps/docs/src/docs/introduction/why.md b/apps/docs/src/docs/introduction/why.md
new file mode 100644
index 0000000..fa00d0f
--- /dev/null
+++ b/apps/docs/src/docs/introduction/why.md
@@ -0,0 +1,55 @@
+# Why Leaf
+
+When it comes to building web applications, there are numerous tools and frameworks at your disposal. Nevertheless, we are convinced that Leaf is the optimal selection for developing powerful, web applications and APIs.
+
+## The problems
+
+While PHP frameworks can make web development faster and more efficient, there are some potential challenges or drawbacks to using them, including:
+
+- ***Learning curve***: Most PHP frameworks have a steep learning curve, especially for developers who are new to the language or the framework's conventions.
+
+- ***Performance overhead***: Some PHP frameworks can add unnecessary performance overhead, due to the additional abstraction layers and features they provide.
+
+- ***Code maintenance***: Most frameworks require adhering to specific coding standards and conventions, which can make maintenance and updates more challenging if you are not already familiar with those standards.
+
+- ***Limited flexibility***: PHP frameworks can be more rigid than writing code from scratch, as they may require you to adhere to specific coding standards and conventions. This can limit your flexibility in terms of how you structure your code and handle specific use cases.
+
+- ***Compatibility with other systems***: Most PHP frameworks are bound to a particular ecosystem and make it difficult to randomly pick and use packages which don't have support for the framework you are using.
+
+- ***Packing a ton of unused code/packages***: Just about every PHP framework out there adds a ton of complexity to your applications in the form of unused code, classes and packages. This in turn leads to bloat and ultimately a drop in performance
+
+## How Leaf tackles these
+
+Leaf 3 provides a bunch of features that aim to tackle these common problems found in just about every PHP framework out there.
+
+### Low barrier to entry
+
+Leaf is the easiest framework to learn with PHP newbies building powerful leaf apps in a few minutes of reading the docs/watching out tutorial videos. All you truly need to get started with Leaf is basic PHP knowledge and optional but recommended knowledge on some backend concepts like JWT auth and more.
+
+### Lightweight
+
+Leaf 2 was lightweight and fast enough to be considered one of the most lightweight but powerful frameworks around, and Leaf 3 makes leaf 2 look like a joke. Leaf 3 can now be considered the most lightweight PHP framework with a source of about 30kb and allows you to build full apps and APIs which end up less than 20mb including user dependencies (leaf api). This is a big haul compared to other frameworks which require dependencies and tons of files which end up more than 100mb.
+
+![image](https://user-images.githubusercontent.com/26604242/146754044-4c71c4ec-7b37-4c85-9c8b-56e8c2b54831.png)
+
+> a comparison with slim - slim (left) - leaf (right)
+
+### Enables high developer productivity
+
+A whole lot of research and testing has been done to build amazing features which allow developers to focus on only what they need: their apps. Leaf 3 has put tons of strategies together to create the best developer experience known to PHP. From things like removing class initializers and creating global functions which allow you call classes from anywhere in your application, modules and other amazing leaf features.
+
+### Powered by [modules](/modules/)
+
+Leaf 3 and its ecosystem are heavily powered by modules, which are simply pieces of leaf's functionality shipped into independently installable libraries. Modules help make leaf even more lightweight and help developers only deal with features which they need in their applications. This means that you only install what you need.
+
+### Easy to use features
+
+As mentioned above, a lot of research has gone into the developer experience for leaf 3 and one aspect was to make our existing features more performant and easier to use. We employed various strategies like modeling some features after popular libraries in other languages and frameworks. For instance, the API for leaf cors is almost an exact replica of the expressjs cors middleware.
+
+### Library/Framework compatibility
+
+Since the beginning of Leaf, we've set out to create code which could easily be integrated with other libraries and frameworks. No matter how powerful leaf is, we try to base of everything we do on simple concepts as opposed to other frameworks which need things like providers in order to access framework features in libraries.
+
+### Scalability
+
+One of the most beautiful things about leaf is that, no matter what package you're using with leaf, if it works in development, it will definitely work in production with near zero config, unless you want some special features. Leaf provides a core and other frameworks/libraries that build around leaf. This makes leaf appropriate for almost any project no matter it's size.
diff --git a/apps/docs/src/docs/leafapi/index.md b/apps/docs/src/docs/leafapi/index.md
new file mode 100644
index 0000000..28158c8
--- /dev/null
+++ b/apps/docs/src/docs/leafapi/index.md
@@ -0,0 +1,121 @@
+# Leaf API
+
+
+
+Leaf API is a minimal but powerful PHP MVC framework. It's designed to be simple, fast and easy to use. Leaf by default doesn't give you a lot of structure, and that's where Leaf API comes in.
+
+Leaf API is a setup that gives you a good starting point for building APIs using the MVC pattern. It's built on top of Leaf, and comes with additional tooling that make building with Leaf even faster.
+
+## Installation
+
+The easiest way to setup Leaf MVC is to use the [Leaf CLI](/docs/cli/):
+
+```bash
+leaf create --api
+```
+
+You can also setup a Leaf API app by using [Composer](https://getcomposer.org/):
+
+```bash
+composer create-project leafs/api
+```
+
+This command will set up a Leaf API app in the `` directory. You can then run the app using the Leaf CLI:
+
+```bash
+cd
+leaf serve
+```
+
+Or the [built-in PHP server](https://www.php.net/manual/en/features.commandline.webserver.php):
+
+```bash
+cd
+php -S localhost:8000
+```
+
+You should then see the welcome page in your browser.
+
+![Leaf API Welcome Page](https://user-images.githubusercontent.com/26604242/224507412-4cb10731-84f2-49a6-a6b8-b9ab5ca7c7b8.png)
+
+## Directory Structure
+
+The Leaf API directory structure is inspired by [Ruby on Rails](https://rubyonrails.org/) and [Laravel](https://laravel.com/). It takes a lot of inspiration from these frameworks, but it's not a clone of either of them. It is meant to be a starting point for building your own applications, and is fully customizable. You can completely change the directory structure to suit your needs, just be sure to update the paths in the `config.php` file.
+
+For a fresh Leaf API app, the directory structure looks like this:
+
+```bash
+C:.
+├───app
+│ ├── console
+│ ├── controllers
+│ ├── database
+│ │ ├── factories
+│ │ ├── migrations
+│ │ ├── schema
+│ │ └── seeds
+│ ├── helpers
+│ ├── models
+│ ├── routes
+│ └── views
+├───config
+├───public
+├───storage
+│ ├───app
+│ │ └───public
+│ ├───framework
+│ │ └───views
+│ └───logs
+└───vendor
+```
+
+- ### The `app` directory
+
+ The `app` directory contains the core code of your application. It's divided into a few sub-directories:
+
+ - `console` - Contains the console commands for your application. These are used to perform tasks on the command line.
+ - `controllers` - Contains the controllers for your application. These are used to handle HTTP requests.
+ - `database` - Contains the database related code for your application. This includes migrations, seeds, factories and schema.
+ - `helpers` - Contains the helper functions for your application.
+ - `models` - Contains the models for your application. These are used to interact with the database.
+ - `routes` - Contains the routes for your application. These are used to map HTTP requests to controllers.
+ - `views` - Contains the views for your application. These are used to render HTML responses.
+
+- ### The `config` directory
+
+ The `config` directory contains the configuration files for your application. These are used to configure how Leaf and it's modules interact with your application. You can find more information about the configuration files in the [Configuration](/docs/mvc/config) section.
+
+- ### The `public` directory
+
+ The `public` directory contains the entry point for your application, and it's also used to serve static assets. The `index.php` file is the entry point for your application. All requests are routed through this file by the web server. This file doesn't contain any application logic, but it does load the Composer autoloader, the application config and all your routes.
+
+- ### The `storage` directory
+
+ The `storage` directory contains the compiled views, logs and other files generated by your application. It's divided into a few sub-directories:
+
+ - `app` - Contains the files generated by your application. This includes the compiled views and the files uploaded by users.
+ - `framework` - Contains the framework generated files for your application.
+ - `logs` - Contains the log files generated by your application.
+
+- ### The `vendor` directory
+
+ The `vendor` directory contains all the dependencies installed by Composer. It's automatically generated when you install the dependencies using Composer.
+
+## Next Steps
+
+Follow along with the next steps to learn more about Leaf API.
+
+
diff --git a/apps/docs/src/docs/leafmvc/index.md b/apps/docs/src/docs/leafmvc/index.md
new file mode 100644
index 0000000..4cbaf7c
--- /dev/null
+++ b/apps/docs/src/docs/leafmvc/index.md
@@ -0,0 +1,127 @@
+# Leaf MVC
+
+
+
+Leaf MVC is a minimal but powerful PHP MVC framework. It's designed to be simple, fast and easy to use. Leaf by default doesn't give you a lot of structure, and that's where Leaf MVC comes in.
+
+Leaf MVC is a setup that gives you a good starting point for building applications using the MVC pattern. It's built on top of Leaf, and comes with additional tooling that make building with Leaf even faster.
+
+## Installation
+
+The easiest way to setup Leaf MVC is to use the [Leaf CLI](/docs/cli/):
+
+```bash
+leaf create --mvc
+```
+
+You can also setup a Leaf MVC app by using [Composer](https://getcomposer.org/):
+
+```bash
+composer create-project leafs/mvc
+```
+
+This command will set up a Leaf MVC app in the `` directory. You can then run the app using the Leaf CLI:
+
+```bash
+cd
+leaf serve
+```
+
+Or the [built-in PHP server](https://www.php.net/manual/en/features.commandline.webserver.php):
+
+```bash
+cd
+php -S localhost:8000
+```
+
+You should then see the welcome page in your browser.
+
+![Leaf MVC Welcome Page](https://user-images.githubusercontent.com/26604242/223189921-d5da1555-bc29-4f99-a3ec-d6cbfdc5350b.png)
+
+## Directory Structure
+
+The Leaf MVC directory structure is inspired by [Ruby on Rails](https://rubyonrails.org/) and [Laravel](https://laravel.com/). It takes a lot of inspiration from these frameworks, but it's not a clone of either of them. It is meant to be a starting point for building your own applications, and is fully customizable. You can completely change the directory structure to suit your needs, just be sure to update the paths in the `config.php` file.
+
+For a fresh Leaf MVC app, the directory structure looks like this:
+
+```bash
+C:.
+├───app
+│ ├── console
+│ ├── controllers
+│ ├── database
+│ │ ├── factories
+│ │ ├── migrations
+│ │ ├── schema
+│ │ └── seeds
+│ ├── helpers
+│ ├── models
+│ ├── routes
+│ └── views
+│ └── errors
+├───config
+├───public
+│ └───assets
+│ ├── css
+│ └── img
+├───storage
+│ ├───app
+│ │ └───public
+│ ├───framework
+│ │ └───views
+│ └───logs
+└───vendor
+```
+
+- ### The `app` directory
+
+ The `app` directory contains the core code of your application. It's divided into a few sub-directories:
+
+ - `console` - Contains the console commands for your application. These are used to perform tasks on the command line.
+ - `controllers` - Contains the controllers for your application. These are used to handle HTTP requests.
+ - `database` - Contains the database related code for your application. This includes migrations, seeds, factories and schema.
+ - `helpers` - Contains the helper functions for your application.
+ - `models` - Contains the models for your application. These are used to interact with the database.
+ - `routes` - Contains the routes for your application. These are used to map HTTP requests to controllers.
+ - `views` - Contains the views for your application. These are used to render HTML responses.
+
+- ### The `config` directory
+
+ The `config` directory contains the configuration files for your application. These are used to configure how Leaf and it's modules interact with your application. You can find more information about the configuration files in the [Configuration](/docs/mvc/config) section.
+
+- ### The `public` directory
+
+ The `public` directory contains the entry point for your application, and it's also used to serve static assets. The `index.php` file is the entry point for your application. All requests are routed through this file by the web server. This file doesn't contain any application logic, but it does load the Composer autoloader, the application config and all your routes.
+
+ There is also an `assets` directory found in the `public` directory. It contains the static assets for your application. These are served by the web server and are accessible to users.
+
+- ### The `storage` directory
+
+ The `storage` directory contains the compiled views, logs and other files generated by your application. It's divided into a few sub-directories:
+
+ - `app` - Contains the files generated by your application. This includes the compiled views and the files uploaded by users.
+ - `framework` - Contains the framework generated files for your application.
+ - `logs` - Contains the log files generated by your application.
+
+- ### The `vendor` directory
+
+ The `vendor` directory contains all the dependencies installed by Composer. It's automatically generated when you install the dependencies using Composer.
+
+## Next Steps
+
+Follow along with the next steps to learn more about Leaf MVC.
+
+
diff --git a/apps/docs/src/docs/migration/introduction.md b/apps/docs/src/docs/migration/introduction.md
new file mode 100644
index 0000000..86fa8a2
--- /dev/null
+++ b/apps/docs/src/docs/migration/introduction.md
@@ -0,0 +1,209 @@
+# Migration Guide
+
+
+
+
+
+This guide is primarily for users with prior Leaf 2 experience who want to learn about the new features and changes in Leaf 3. **This is not something you have to read from top to bottom before trying out Leaf 3.** While it looks like a lot has changed, a lot of what you know and love about Leaf is still the same; but we wanted to be as thorough as possible and provide detailed explanations and examples for every documented change.
+
+::: warning Coming from another library
+Migrating from another framework? [READ THIS](/docs/migration/other) to get started.
+:::
+
+- [Quickstart](#quickstart)
+- [Breaking Changes](#breaking-changes)
+- [Notable New Features](#notable-new-features)
+
+
+
+
+## Quickstart
+
+If you want to quickly try out Leaf 3 in a new project, create a folder and run:
+
+```bash
+composer require leafs/leaf
+```
+
+This will quickly setup a leaf 3 with the default modules. From there, create your `index.php` file and add this quickstart.
+
+
+
+You can run this with the built in php server
+
+```bash
+php -S localhost:5500
+```
+
+Alternatively, you can use the Leaf CLI:
+
+```bash
+leaf create
+```
+
+And run the sample app with:
+
+```bash
+leaf serve
+```
+
+### Migrating from leaf 2
+
+As mentioned before, we've made leaf 3 as backwards compatible with Leaf 2.5+ as possible. This means that moving from v2 to v3 will be a breeze or close.
+
+- Install leaf 3
+
+```bash
+composer require leafs/leaf
+```
+
+Or with leaf CLI
+
+```bash
+leaf install leaf
+```
+
+::: tip Watch out
+You should probably delete your `vendor` folder and `package-lock.json` before running the command above to make sure that all the dependencies are accurately reinstalled.
+:::
+
+- After this, it's just a matter of installing the modules required in your project.
+For example, if you use `Leaf\Auth`, you will need to install the auth module. This can be done with:
+
+```bash
+leaf install auth
+```
+
+Or with composer:
+
+```bash
+composer require leafs/auth
+```
+
+Just do this for all other modules in your project. And your app should be back online, working even faster than before.
+
+## Breaking Changes
+
+The following consists a list of breaking changes from 2.x:
+
+### Modules
+
+Leaf 3 only retains the core of the framework with a few utilities, all other features were packaged as modules. This means that you will have errors if you try to use some packages like `Leaf\Auth` or `Leaf\Flash` without installing them first.
+
+This is not really a problem since installing the module will automatically fix any problems that came along with it's abscence.
+
+### CORS
+
+In v2, some basic cors configuration was available on the Leaf object, however, this has been discontinued and replaced with the Cors module. This module contains both basic and advanced CORS configurations and is inspired by the ExpressJS cors package. So if you have any experience with that library, you will have no problems using the leaf cors module.
+
+To fix any problems with cors in your Leaf 2 app, follow these steps:
+
+- Install the cors module
+
+```bash
+composer require leafs/cors
+```
+
+- Replace the original cors configuration with the cors module. (This is done under the hood for you, all you need to do now call the cors method on your leaf app)
+
+
+
+The cors method is automatically linked to the cors module by Leaf and so, no extra configuration is needed to make it work. Cors takes in some optional configuration, checkout the [cors module docs](/modules/cors/). Also cors is no longer available on the response object.
+
+### Router
+
+`Leaf\Router::getRequestMethod` has been been moved to `Leaf\Http\Request::getMethod`. This is used in Leaf's core and should not be an issue, but if you do have references to this function, changing it to `Leaf\Http\Request::getMethod` will fix any errors.
+
+## Notable New Features
+
+Some of the new features to keep an eye on in Leaf 3 include:
+
+- [Global functions](/docs/tooling/functions)
+- [CORS module](/modules/cors/)
diff --git a/apps/docs/src/docs/migration/leafapi.md b/apps/docs/src/docs/migration/leafapi.md
new file mode 100644
index 0000000..152e818
--- /dev/null
+++ b/apps/docs/src/docs/migration/leafapi.md
@@ -0,0 +1,130 @@
+# Introduction
+
+::: tip
+Leaf 3 is not yet available with Leaf 3. This means that you need to add Leaf 3 to your project manually.
+:::
+
+This guide is primarily for Leaf API users with prior Leaf 2 experience who want to learn about the new features and changes in Leaf 3.
+
+- [Quickstart](#quickstart)
+- [Breaking Changes](#breaking-changes)
+- [Notable New Features](#notable-new-features)
+- [Supporting Libraries](#supporting-libraries)
+
+
+
+## Quickstart
+
+If you want to quickly try out Leaf 3 in a new project:
+
+```bash
+composer require leafs/leaf v3.x-dev
+```
+
+This will quickly setup leaf 3 with the default modules. From there, create your `index.php` file and add this quickstart.
+
+```php
+get("/", function () {
+ app()->response(["name" => "Leaf"]);
+});
+
+app()->run();
+```
+
+You can run this with the built in php server
+
+```bash
+php -S localhost:5500
+```
+
+### Migrating from leaf 2
+
+As mentioned before, we've made leaf 3 as backwards compatible with Leaf 2.5+ as possible. This means that moving from v2 to v3 will be a breeze or close.
+
+::: warning
+Note that leaf 3 is still under active development. We don't recommend switching to Leaf 3 yet for production ready apps. You can go ahead if it's a personal project or just want to try out leaf 3.
+:::
+
+- Install leaf 3
+
+```bash
+composer require leafs/leaf v3.x-dev
+```
+
+> You can delete your vendor folder before running the command above to make sure that all the dependencies are accurately reinstalled.
+
+- After this, it's just a matter of installing the modules required in your project.
+For example, if you use `Leaf\Auth`, you will need to install the auth module. This can be done with:
+
+```bash
+composer require leafs/auth
+```
+
+Just do this for all other modules in your project. And your app should be back online, working even faster than before.
+
+## Breaking Changes
+
+The following consists a list of breaking changes from 2.x:
+
+### Modules
+
+Leaf 3 only retains the core of the framework with a few utilities, all other features were packaged as modules. This means that you will have errors if you try to use some packages like `Leaf\Auth` or `Leaf\Flash` without installing them first.
+
+This is not really a problem since installing the module will automatically fix any problems that came along with it's abscence.
+
+### CORS
+
+In v2, some basic cors configuration was available on the Leaf object, however, this has been discontinued and replaced with the Cors module. This module contains both basic and advanced CORS configurations and is inspired by the ExpressJS cors package. So if you have any experience with that library, you will have no problems using the leaf cors module.
+
+To fix any problems with cors in your Leaf 2 app, follow these steps:
+
+- Install the cors module
+
+```bash
+composer require leafs/cors
+```
+
+- Replace the original cors configuration with the cors module. (This is done under the hood for you, all you need to do now call the cors method on your leaf app)
+
+Replace this:
+
+```php
+$app = new Leaf\App;
+
+$app->evadeCors(true);
+
+// ...
+```
+
+with...
+
+```php
+$app = new Leaf\App;
+
+$app->cors();
+
+// ...
+```
+
+The cors method is automatically linked to the cors module by Leaf and so, no extra configuration is needed to make it work. Cors takes in some optional configuration, checkout the [cors module docs](/modules/cors/). Also cors is no longer available on the response object.
+
+### Router
+
+`Leaf\Router::getRequestMethod` has been been moved to `Leaf\Http\Request::getMethod`. This is used in Leaf's core and should not be an issue, but if you do have references to this function, changing it to `Leaf\Http\Request::getMethod` will fix any errors.
+
+## Notable New Features
+
+Some of the new features to keep an eye on in Leaf 3 include:
+
+
+
+- [CORS module](/modules/cors/)
diff --git a/apps/docs/src/docs/migration/leafmvc.md b/apps/docs/src/docs/migration/leafmvc.md
new file mode 100644
index 0000000..e69de29
diff --git a/apps/docs/src/docs/migration/other.md b/apps/docs/src/docs/migration/other.md
new file mode 100644
index 0000000..8fed725
--- /dev/null
+++ b/apps/docs/src/docs/migration/other.md
@@ -0,0 +1,118 @@
+---
+aside: none
+---
+
+# Migrating from other frameworks
+
+
+
+This page is for developers who have a working application in another framework and want to port over to Leaf. As far-fetched as this sounds, Leaf 3 makes it super easy to sprinkle pieces of Leaf into any existing application, gradually rewriting it without breaking any code. Leaf has always allowed users to integrate other libraries seamlessly into their leaf apps with no conflicts or complexities, now Leaf 3 allows you to go the other way: **integrating Leaf seamlessly into any application no matter which libraries or frameworks it was built with.**
+
+
+
+## Quickstart
+
+All of Leaf's features are now available as modules, this means that if there's a particular feature of Leaf you will love to try out, there's no need to pack the whole framework anymore. Just find that feature you want and install it.
+
+Below is a [Slim PHP 4](https://www.slimframework.com/) application which we want to use Leaf 3 in:
+
+```php
+get('/', function (Request $request, Response $response, $args) {
+ $name = $args['name'];
+ $response->getBody()->write("Hello, $name");
+ return $response;
+});
+
+$app->run();
+```
+
+We can start off by swapping out the Slim request and response objects with Leaf's. We can achieve this by replacing only lines 11-13 with Leaf.
+
+```php{2-3,11-13}
+get('/', function (Request $request, Response $response, $args) {
+ $name = $args['name'];
+ $response->getBody()->write("Hello, $name");
+ return $response;
+});
+
+$app->run();
+```
+
+All we have to do now is to install the leaf http module.
+
+```bash
+composer require leafs/http
+```
+
+Now, we replace Slim's http handlers with Leaf's.
+
+
+
+In all aspects, this is still a slim 4 app, only using the Leaf request and response objects. Just as with the example above, you can use any Leaf module or Leaf 3 itself with any framework or library. To test the app above, we can use the built in php server
+
+```bash
+php -S localhost:5500
+```
diff --git a/apps/docs/src/docs/migration/skeleton.md b/apps/docs/src/docs/migration/skeleton.md
new file mode 100644
index 0000000..e69de29
diff --git a/apps/docs/src/docs/mvc/commands.md b/apps/docs/src/docs/mvc/commands.md
new file mode 100644
index 0000000..6382aa8
--- /dev/null
+++ b/apps/docs/src/docs/mvc/commands.md
@@ -0,0 +1,400 @@
+# Writing Commands
+
+Leaf MVC and Leaf API come with a [built-in command line](/docs/mvc/console) interface named Aloe. This CLI is used to run commands and perform tasks like creating controllers, models, migrations, seeds, etc.
+
+In addition to the built-in commands, you can also create your own commands. This is useful if you want to create a command that performs a specific task in your application.
+
+## Generating a command
+
+The easiest way to create a command is to use the `g:command` command. This command will create a new command class in your `app/console` directory.
+
+```bash
+php leaf g:command CachePurge
+```
+
+This will create a `CachePurgeCommand` in the `app/console` directory. Instead of using the class name, you can also create commands using the command you want to run in your console like this:
+
+```bash
+php leaf g:command cache:purge
+```
+
+This will create a `CachePurgeCommand` in the `app/console` directory.
+
+Using the `g:command` command also registers the commands in Aloe. This means you can run the command immediately after creating it.
+
+## Manually writing a command
+
+If you don't use the `g:command` command, you can create a command manually. To do this, create a new class in your `app/console` directory and extend the `Aloe\Command` class. The class should also have `$defaultName` and `$description` properties.
+
+The `$defaultName` property is the name of the command that will be used to run the command in the console. The `$description` property is a short description of what the command does.
+
+The class should have a `handle()`. This method is called when the command is run in the console. The `handle` method should return `0` if the command was successful and `1` if it failed.
+
+```php
+setArgument('argument', 'optional', 'argument description')
+ ->setOption('option', 'o', 'required', 'option description');
+ }
+
+ protected function handle()
+ {
+ $this->comment(
+ "example command's output {$this->argument('argument')} {$this->option('option')}"
+ );
+
+ return 0;
+ }
+}
+```
+
+Both Leaf MVC and Leaf API ship with an example command. You can find it in the `app/console` directory.
+
+::: tip Symfony Console
+Aloe is built on top of Symfony Console. This means you can use all of the features of Symfony Console in your commands. You can read more about Symfony Console [here](https://symfony.com/doc/current/components/console.html).
+:::
+
+## Registering Commands
+
+By default, aloe cli registers all generated commands, however, if you create a command manually, you'll have to register it manually. There are also situations where a package might need you to register a command, it can also be done using same method.
+
+To add your commands, open up the `leaf` file in the root directory of your project. You'll find a commented section talking about custom commands.
+
+```php
+/*
+|--------------------------------------------------------------------------
+| Add custom command
+|--------------------------------------------------------------------------
+|
+| If you have a new command to add to Leaf
+|
+*/
+$console->register(\App\Console\ExampleCommand::class);
+```
+
+An example command has already been registered, so you can follow this example. You can call the `register` method on the `$console` variable. The `register` method takes in the command class as a parameter.
+
+```php
+$console->register(\App\Console\YourCommand::class);
+```
+
+You can also pass in an array of commands to register, as such, a custom package with a couple of commands to register can simply return an array of all those commands.
+
+```php
+$console->register([
+ \App\Console\AppCommand::class,
+ \App\Console\AppCommand2::class,
+]);
+
+$console->register(CustomPackage::commands());
+```
+
+## Command Arguments
+
+Command arguments are values that are passed to the command when it is run in the console. For example, if you have a command named `example` and you run it like this:
+
+```bash
+php leaf example argument
+```
+
+The `argument` value is an argument that is passed to the command. You can access the argument in the `config()` method using the `setArgument()` method. It typically follows the same convention as symfony console's `addArgument` except that instead of passing in `InputArgument::state`, you just pass in the state as a string. For example, instead of `InputArgument::REQUIRED`, you just pass in `"required"`, any case is supported.
+
+```php
+protected function config()
+{
+ $this->setArgument('argument', 'required', 'argument description');
+}
+```
+
+You can access the argument in the `handle()` method using the `argument()` method.
+
+```php
+protected function handle()
+{
+ $this->comment("example command's output {$this->argument('argument')}");
+}
+```
+
+## Command Options
+
+Command options are values that are passed to the command when it is run in the console. For example, if you have a command named `example` and you run it like this:
+
+```bash
+php leaf example --option=value
+```
+
+To add an option to your command, you can use the `setOption()` method in the `config()` method. It typically follows the same convention as symfony console's `addOption` except that instead of passing in `InputOption::state`, you just pass in the state as a string. For example, instead of `InputOption::VALUE_REQUIRED`, you just pass in `"required"`, any case is supported.
+
+```php
+protected function config()
+{
+ $this->setOption('option', 'o', 'required', 'option description');
+}
+```
+
+You can access the option in the `handle()` method using the `option()` method.
+
+```php
+protected function handle()
+{
+ $this->comment("example command's output {$this->option('option')}");
+}
+```
+
+## Command Input
+
+Aloe makes it easier to grab the Symfony input object from anywhere in your command. This means that you don't have to pass in the `$input` variable to the `handle()` method. Instead, you can use the `input()` method.
+
+```php
+public function handle()
+{
+ $input = $this->input();
+ $name = $input->getArgument('name');
+}
+```
+
+## Command Output
+
+Aloe makes it easier to grab and output text to the console from anywhere in your command. Unlike with symfony console, you don't have to pass in the `$output` variable to the `handle()` method. Instead, you can use `ouput()`, `write()`, `writeln()`, `comment()`, `info()`, `error()`, `question()` and `link()` methods.
+
+### output()
+
+This method either outputs text in your console or returns the Symfony output object. If a value is passed into `output()`, it will write the value to the console.
+
+```php
+public function handle()
+{
+ $this->output('Hello World');
+}
+```
+
+If no value is passed into `output()`, it will return the Symfony output object.
+
+```php
+public function handle()
+{
+ $output = $this->output();
+ $output->writeln('This is output');
+}
+```
+
+### write()
+
+This method writes text to the console. It is the same as the `output()->write()` method.
+
+```php
+public function handle()
+{
+ $this->write('Hello World');
+}
+```
+
+### writeln()
+
+This method writes text to the console and adds a new line. It is the same as the `output()->writeln()` method.
+
+```php
+public function handle()
+{
+ $this->writeln('Hello World');
+}
+```
+
+### comment()
+
+This method writes a comment styled message to the console and adds a new line. It is the same as the `output()->writeln()` method with the `SymfonyStyle::COMMENT` style.
+
+```php
+public function handle()
+{
+ $this->comment('Hello World');
+}
+```
+
+### info()
+
+This method writes an info styled message to the console and adds a new line. It is the same as the `output()->writeln()` method with the `SymfonyStyle::INFO` style.
+
+```php
+public function handle()
+{
+ $this->info('Hello World');
+}
+```
+
+### error()
+
+This method writes an error styled message to the console and adds a new line. It is the same as the `output()->writeln()` method with the `SymfonyStyle::ERROR` style.
+
+```php
+public function handle()
+{
+ $this->error('Hello World');
+}
+```
+
+### question()
+
+This method writes a question styled message to the console and adds a new line. It is the same as the `output()->writeln()` method with the `SymfonyStyle::QUESTION` style.
+
+```php
+public function handle()
+{
+ $this->question('Hello World');
+}
+```
+
+### link()
+
+This method writes a link to the console and adds a new line.
+
+```php
+public function handle()
+{
+ $this->link('https://leafphp.dev', 'Leaf PHP');
+}
+```
+
+## Command Questions
+
+Aloe makes it easier to ask questions in your command. You can use the `ask()`, `confirm()`, `askRaw()`, `autoComplete()`, `choice()` and `multiChoice()` methods.
+
+### ask()
+
+This method asks a question and returns the answer. It takes in 2 parameters:
+
+- the question to ask
+- the default answer (optional)
+
+```php
+public function handle()
+{
+ $name = $this->ask('What is your name?', 'Leaf');
+}
+```
+
+### askRaw()
+
+This is the same as the `ask()` method above, except that it does not trim the results that the user enters. Whatever the user enters is returned as is.
+
+```php
+public function handle()
+{
+ $name = $this->askRaw('What is your name?', 'Leaf');
+}
+```
+
+### autoComplete()
+
+This method allows you to ask a question and provide a list of values that the user can choose from. The user's answer will be auto-completed as they type if it matches one of the values in the list. It takes in 3 parameters:
+
+- the question to ask
+- the list of values to choose from
+- the default answer (optional)
+
+```php
+public function handle()
+{
+ $name = $this->autoComplete('What is your name?', ['Leaf', 'PHP'], 'Leaf');
+}
+```
+
+### choice()
+
+This method allows you to ask a question and provide a list of values that the user can choose from. The user must select one of the values in the list. It takes in 4 parameters:
+
+- the question to ask
+- the list of values to choose from
+- the error message to display if the user does not select one of the values in the list
+- the default answer (optional)
+
+```php
+public function handle()
+{
+ $name = $this->choice('What is your name?', ['Leaf', 'PHP'], 'Please select a name');
+}
+```
+
+### multiChoice()
+
+This method allows you to ask a question and provide a list of values that the user can choose from. The user must select one or more of the values in the list. It takes in 4 parameters:
+
+- the question to ask
+- the list of values to choose from
+- the error message to display if the user does not select one of the values in the list
+- the default answer (optional)
+
+```php
+public function handle()
+{
+ $name = $this->multiChoice('What is your name?', ['Leaf', 'PHP'], 'Please select a name');
+}
+```
+
+### confirm()
+
+This method asks a yes/no question and returns the answer. It takes in 2 parameters:
+
+- the question to ask
+- the default answer (optional)
+
+```php
+public function handle()
+{
+ $name = $this->confirm('Are you sure?', 'yes');
+}
+```
+
+### secret()
+
+This method asks a question but hides the keystrokes. It takes in 2 parameters:
+
+- the question to ask
+- use hidden fallback (optional)
+
+```php
+$password = $this->secret('Confirm your password');
+```
+
+## Aloe Installer
+
+Aloe installer allows you to quickly install files and routes from your library into your Leaf MVC or Leaf API project's working directory.
+
+### Magic Copy
+
+This method allows you to auto-magically copy all files and folders from a specified folder into Leaf workspace.
+
+```php
+Aloe\Installer::magicCopy("package/to/install");
+```
+
+Consider the following directory structure:
+
+```bash
+C:.
+└───Auth
+ ├───controllers
+ ├───routes
+ └───views
+```
+
+To copy our controllers, routes and views, we simply need to point `magicCopy` to the auth directory.
+
+```php
+\Aloe\Installer::magicCopy('package/Auth');
+```
+
+This will copy the sub directories in Auth to the `app` folder in the working directory. This is especially useful in team projects where you want to share resources with your team. You can simply create a package with the resources you want to share and then use `magicCopy` to copy them into the working directory.
diff --git a/apps/docs/src/docs/mvc/config.md b/apps/docs/src/docs/mvc/config.md
new file mode 100644
index 0000000..4c2bb11
--- /dev/null
+++ b/apps/docs/src/docs/mvc/config.md
@@ -0,0 +1,734 @@
+# Configuration
+
+
+
+Both Leaf MVC and Leaf API try to maintain a working out-of-the-box configuration as much as possible, so, for the most part, you don't have to configure anything. However, there are some things you may want to customize to match your application's style, and this page will show you how.
+
+## Overview
+
+You can find your application in the `config` directory. This directory contains all the configuration files for you need for different features. You only need to configure the files that you need to configure. If you don't need to configure a file, you can just leave it as is. Each option is documented, so feel free to look through the files and get familiar with the options available to you.
+
+These configuration files allow you to configure things like your database connection information, your mail server information, as well as various other core configuration values such as your application timezone and encryption key.
+
+For the most part, you will only need to set the values in the `.env` file.
+
+## The `.env` file
+
+Having different configuration values based on the environment in which an application is operating is usually pretty helpful. For instance, you may need to use a different database for local development compared to your production server.
+
+The `.env` file allows you to define environment specific configuration values. You can define as many environment variables as you want in this file. The values in this file will be loaded into the `$_ENV` superglobal variable, and will be accessible via the `_env()` helper function.
+
+In a new Leaf installation, the root folder of your application will include an `.env.example` file that specifies numerous typical environment variables. As part of the Leaf installation process, this file will be automatically duplicated to `.env`. You may then modify the `.env` file to suit your needs. The `.env` file is not tracked by Git, so you can safely modify it without worrying about it being overwritten by future updates.
+
+::: tip Managing Environment Variables
+
+If you add new environment variables to a team project, be sure to add the keys to the `.env.example` file so that other developers will know what environment variables are available.
+
+:::
+
+*If you want more info on how Leaf handles your application's environment, you can check out the [environment docs](/docs/config/nsm).*
+
+## Application Config
+
+This configuration basically controls how Leaf works with your application. This file contains the following options by default:
+
+```php
+ _env('APP_DOWN', false),
+
+ /*
+ |--------------------------------------------------------------------------
+ | App debugging
+ |--------------------------------------------------------------------------
+ |
+ | If debugging is enabled, Leaf will use its built-in error handler to
+ | display diagnostic information for uncaught Exceptions, else it will
+ | display a bare error page usable in production. You can set a
+ | custom error page to display using `$app->setError`.
+ |
+ | You might want to turn this off in production.
+ |
+ */
+ 'debug' => _env('APP_DEBUG', true),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Log directory
+ |--------------------------------------------------------------------------
+ |
+ | This tells leaf which directory to save and look for logs.
+ |
+ */
+ 'log.dir' => 'storage/logs/',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Log Enabled
+ |--------------------------------------------------------------------------
+ |
+ | This enables or disables Leaf’s logger. Note that if log.enabled is
+ | set to false. Leaf will skip initializing anything related to logs,
+ | as such, you won't have access to $app->logger(),
+ | $app->log or $app->logWriter.
+ |
+ */
+ 'log.enabled' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Log file
+ |--------------------------------------------------------------------------
+ |
+ | This setting tells leaf which file to write logs to.
+ |
+ */
+ 'log.file' => 'app.log',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Log level
+ |--------------------------------------------------------------------------
+ |
+ | Leaf has these log levels:
+ |
+ | - \Leaf\Log::EMERGENCY
+ | - \Leaf\Log::ALERT
+ | - \Leaf\Log::CRITICAL
+ | - \Leaf\Log::ERROR
+ | - \Leaf\Log::WARN
+ | - \Leaf\Log::NOTICE
+ | - \Leaf\Log::INFO
+ | - \Leaf\Log::DEBUG
+ |
+ */
+ 'log.level' => \Leaf\Log::DEBUG,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Log open
+ |--------------------------------------------------------------------------
+ |
+ | Takes in a boolean and determines whether Leaf should create
+ | the specified log file if it doesn't exist.
+ |
+ */
+ 'log.open' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Log writer
+ |--------------------------------------------------------------------------
+ |
+ | Use a custom log writer to direct logged messages
+ | to the appropriate output destination.
+ |
+ */
+ 'log.writer' => null,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Mode
+ |--------------------------------------------------------------------------
+ |
+ | This is an identifier for the application’s current mode of operation.
+ | The mode does not affect a Leaf application’s internal functionality.
+ |
+ */
+ 'mode' => 'development',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Views path
+ |--------------------------------------------------------------------------
+ |
+ | The relative or absolute path to the filesystem directory that
+ | contains your Leaf application’s view files.
+ |
+ */
+ 'views.path' => ViewsPath(null, false),
+
+ /*
+ |--------------------------------------------------------------------------
+ | views cache path
+ |--------------------------------------------------------------------------
+ |
+ | This config tells leaf where to save cached and compiled views.
+ |
+ */
+ 'views.cachePath' => StoragePath('framework/views')
+];
+```
+
+You can find more information about these settings in the [App Config](/docs/config/settings.html) documentation.
+
+## Aloe CLI Config
+
+This configuration file is used to configure the Aloe CLI. This file basically contains a map of paths that directs Aloe where to find framework files. This file contains the following options by default:
+
+```php
+// config for aloe CLI
+return [
+ 'paths' => [
+ 'controllers_path' => '/app/controllers',
+ 'models_path' => '/app/models',
+ 'migrations_path' => '/app/database/migrations',
+ 'seeds_path' => '/app/database/seeds',
+ 'factories_path' => '/app/database/factories',
+ 'helpers_path' => '/app/helpers',
+ 'views_path' => '/app/views',
+ 'config_path' => '/app/config',
+ 'storage_path' => '/storage',
+ 'commands_path' => '/app/console',
+ 'routes_path' => '/app/routes',
+ 'lib_path' => '/lib',
+ ],
+];
+```
+
+You only need to configure this file if you want to change the default paths.
+
+## Auth Config
+
+This configuration file is used to configure [Leaf Auth](/modules/auth/). You only need to update this config if you want to switch up the way Leaf Auth works out of the box. This file contains the following options by default:
+
+```php
+use Leaf\Helpers\Password;
+
+return [
+ /*
+ |--------------------------------------------------------------------------
+ | Database table
+ |--------------------------------------------------------------------------
+ |
+ | This is the table that leaf auth will perform authentication
+ | checks on and edit/retrieve users from.
+ |
+ */
+ 'DB_TABLE' => 'users',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Use session
+ |--------------------------------------------------------------------------
+ |
+ | Use session based authentication instead of the default JWT based auth.
+ |
+ */
+ 'USE_SESSION' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Generate timestamps
+ |--------------------------------------------------------------------------
+ |
+ | Automatically generate created_at/updated_at timestamps for register
+ | and update methods
+ |
+ */
+ 'USE_TIMESTAMPS' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Encode password
+ |--------------------------------------------------------------------------
+ |
+ | Password encode is run when leaf wants to encode passwords on register
+ | This exact method is used by default in Leaf, so you can set it to null
+ | if you want to.
+ |
+ | You can set your own implementation instead of Password::hash
+ |
+ */
+ 'PASSWORD_ENCODE' => function ($password) {
+ return Password::hash($password);
+ },
+
+ /*
+ |--------------------------------------------------------------------------
+ | Verify Password
+ |--------------------------------------------------------------------------
+ |
+ | This function is run to verify the password. This implementation is done
+ | by default, so you can set it to null, and it will still work fine.
+ |
+ | You can add your own implementation instead of Password::verify
+ |
+ */
+ 'PASSWORD_VERIFY' => function ($password, $hashedPassword) {
+ return Password::verify($password, $hashedPassword);
+ },
+
+ /*
+ |--------------------------------------------------------------------------
+ | Password Key
+ |--------------------------------------------------------------------------
+ |
+ | The default password key. Leaf will expect this key to hold passwords
+ | in your database.
+ |
+ */
+ 'PASSWORD_KEY' => 'password',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Hide id
+ |--------------------------------------------------------------------------
+ |
+ | Hide id field from user object returned in login, register and update
+ |
+ */
+ 'HIDE_ID' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Hide password
+ |--------------------------------------------------------------------------
+ |
+ | Hide password from user object returned in login, register and update
+ |
+ */
+ 'HIDE_PASSWORD' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Login params error
+ |--------------------------------------------------------------------------
+ |
+ | Error to show when the login params aren't found in db
+ |
+ */
+ 'LOGIN_PARAMS_ERROR' => 'Username not registered!',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Password error
+ |--------------------------------------------------------------------------
+ |
+ | Error to show when the login password is wrong
+ |
+ */
+ 'LOGIN_PASSWORD_ERROR' => 'Password is incorrect!',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Session on register
+ |--------------------------------------------------------------------------
+ |
+ | If true, a session will be created on a successful registration, else
+ | you it'll be created on login rather.
+ |
+ */
+ 'SESSION_ON_REGISTER' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Login page route
+ |--------------------------------------------------------------------------
+ */
+ 'GUARD_LOGIN' => '/auth/login',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Register page route
+ |--------------------------------------------------------------------------
+ */
+ 'GUARD_REGISTER' => '/auth/register',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Logout route
+ |--------------------------------------------------------------------------
+ */
+ 'GUARD_LOGOUT' => '/auth/logout',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Home page route
+ |--------------------------------------------------------------------------
+ */
+ 'GUARD_HOME' => '/home',
+
+ /*
+ |--------------------------------------------------------------------------
+ | JWT + Session
+ |--------------------------------------------------------------------------
+ |
+ | Add an auth token to the auth session?
+ |
+ */
+ 'SAVE_SESSION_JWT' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | JWT Token Secret
+ |--------------------------------------------------------------------------
+ |
+ | Secret string to encode JWT
+ |
+ */
+ 'TOKEN_SECRET' => '@_leaf$0Secret!',
+
+ /*
+ |--------------------------------------------------------------------------
+ | JWT Lifetime
+ |--------------------------------------------------------------------------
+ |
+ | How long should JWT be valid for?
+ |
+ */
+ 'TOKEN_LIFETIME' => 60 * 60 * 24 * 365
+];
+```
+
+You can find more information about these settings in the [Auth Config](/modules/auth/v/2.1/config.html) documentation.
+
+## CORS config
+
+This config allows you pass options to the [Leaf CORS module](/modules/cors/). The default config looks like this:
+
+```php
+return [
+ /*
+ |--------------------------------------------------------------------------
+ | Configure allowed origins
+ |--------------------------------------------------------------------------
+ |
+ | Configures the Access-Control-Allow-Origin CORS header. Possible values:
+ |
+ | * String - set origin to a specific origin. For example if
+ | you set it to "http://example.com" only requests from
+ | "http://example.com" will be allowed.
+ |
+ | * RegExp - set origin to a regular expression pattern which will be
+ | used to test the request origin. If it's a match, the request origin
+ | will be reflected. For example the pattern /example\.com$/ will reflect
+ | any request that is coming from an origin ending with "example.com".
+ |
+ | * Array - set origin to an array of valid origins. Each origin can be a String
+ | or a RegExp. For example ["http://example1.com", /\.example2\.com$/] will
+ | accept any request from "http://example1.com" or from
+ | a subdomain of "example2.com".
+ |
+ | * Function - set origin to a function implementing some custom
+ | logic. The function takes the request origin as the first parameter
+ | and a callback (called as callback(err, origin), where origin is a
+ | non-function value of the origin option) as the second.
+ |
+ */
+ 'origin' => '*',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Configure allowed HTTP methods
+ |--------------------------------------------------------------------------
+ |
+ | Configures the Access-Control-Allow-Methods CORS header.
+ | Expects a comma-delimited string (ex: 'GET,PUT,POST') or
+ | an array (ex: ['GET', 'PUT', 'POST'])
+ |
+ */
+ 'methods' => 'GET,HEAD,PUT,PATCH,POST,DELETE',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Configure allowed HTTP headers
+ |--------------------------------------------------------------------------
+ |
+ | Configures the Access-Control-Allow-Headers CORS header. Expects a
+ | comma-delimited string (ex: 'Content-Type,Authorization') or
+ | an array (ex: ['Content-Type', 'Authorization']). If not specified,
+ | defaults to reflecting the headers specified in the request's
+ | Access-Control-Request-Headers header.
+ |
+ */
+ 'allowedHeaders' => '*',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Configure expose headers
+ |--------------------------------------------------------------------------
+ |
+ | Configures the Access-Control-Expose-Headers CORS header. Expects
+ | a comma-delimited string (ex: 'Content-Range,X-Content-Range')
+ | or an array (ex: ['Content-Range', 'X-Content-Range']).
+ | If not specified, no custom headers are exposed.
+ |
+ */
+ 'exposedHeaders' => '',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Configure credentials
+ |--------------------------------------------------------------------------
+ |
+ | Configures the Access-Control-Allow-Credentials CORS header.
+ | Set to true to pass the header, otherwise it is omitted.
+ |
+ */
+ 'credentials' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Configure max age
+ |--------------------------------------------------------------------------
+ |
+ | Configures the Access-Control-Max-Age CORS header. Set to
+ | an integer to pass the header, otherwise it is omitted.
+ |
+ */
+ 'maxAge' => null,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Configure preflight continue
+ |--------------------------------------------------------------------------
+ |
+ | Pass the CORS preflight response to the next handler.
+ |
+ */
+ 'preflightContinue' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Log open
+ |--------------------------------------------------------------------------
+ |
+ | Provides a status code to use for successful OPTIONS requests,
+ | since some legacy browsers (IE11, various SmartTVs) choke on 204.
+ |
+ */
+ 'optionsSuccessStatus' => 204,
+];
+```
+
+You can find more information on the configuration options on the [cors config docs](/modules/cors/#configuration-options).
+
+## Database config
+
+This config allows you to configure the database connection. The default config looks like this:
+
+```php
+return [
+
+ /*
+ |--------------------------------------------------------------------------
+ | Default Database Connection Name
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify which of the database connections below you wish
+ | to use as your default connection for all database work. Of course
+ | you may use many connections at once using the Database library.
+ |
+ */
+
+ 'default' => _env('DB_CONNECTION', 'mysql'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Database Connections
+ |--------------------------------------------------------------------------
+ |
+ | Here are each of the database connections setup for your application.
+ | Of course, examples of configuring each database platform that is
+ | supported by eloquent is shown below to make development simple.
+ |
+ |
+ | All database work in eloquent is done through the PHP PDO facilities
+ | so make sure you have the driver for your particular database of
+ | choice installed on your machine before you begin development.
+ |
+ */
+
+ 'connections' => [
+ 'sqlite' => [
+ 'driver' => 'sqlite',
+ 'url' => _env('DATABASE_URL'),
+ 'database' => _env('DB_DATABASE', DatabasePath('database.sqlite')),
+ 'prefix' => '',
+ 'foreign_key_constraints' => _env('DB_FOREIGN_KEYS', true),
+ ],
+
+ 'mysql' => [
+ 'driver' => 'mysql',
+ 'url' => _env('DATABASE_URL'),
+ 'host' => _env('DB_HOST', '127.0.0.1'),
+ 'port' => _env('DB_PORT', '3306'),
+ 'database' => _env('DB_DATABASE', 'forge'),
+ 'username' => _env('DB_USERNAME', 'forge'),
+ 'password' => _env('DB_PASSWORD', ''),
+ 'unix_socket' => _env('DB_SOCKET', ''),
+ 'charset' => _env('DB_CHARSET', 'utf8mb4'),
+ 'collation' => _env('DB_COLLATION', 'utf8mb4_unicode_ci'),
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ 'strict' => true,
+ 'engine' => null,
+ 'options' => extension_loaded('pdo_mysql') ? array_filter([
+ PDO::MYSQL_ATTR_SSL_CA => _env('MYSQL_ATTR_SSL_CA'),
+ ]) : [],
+ ],
+
+ 'pgsql' => [
+ 'driver' => 'pgsql',
+ 'url' => _env('DATABASE_URL'),
+ 'host' => _env('DB_HOST', '127.0.0.1'),
+ 'port' => _env('DB_PORT', '5432'),
+ 'database' => _env('DB_DATABASE', 'forge'),
+ 'username' => _env('DB_USERNAME', 'forge'),
+ 'password' => _env('DB_PASSWORD', ''),
+ 'charset' => _env('DB_CHARSET', 'utf8'),
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ 'schema' => 'public',
+ 'sslmode' => 'prefer',
+ ],
+
+ 'sqlsrv' => [
+ 'driver' => 'sqlsrv',
+ 'url' => _env('DATABASE_URL'),
+ 'host' => _env('DB_HOST', 'localhost'),
+ 'port' => _env('DB_PORT', '1433'),
+ 'database' => _env('DB_DATABASE', 'forge'),
+ 'username' => _env('DB_USERNAME', 'forge'),
+ 'password' => _env('DB_PASSWORD', ''),
+ 'charset' => _env('DB_CHARSET', 'utf8'),
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ ],
+ ],
+];
+```
+
+This config has been modified to use the `_env` helper function. This helper function will check if the environment variable is set and if not, it will return the default value. This is useful for when you want to use the same config file for both development and production. Leaf will automatically use the connection details from the selected db type (`DB_CONNECTION`) in the `.env` file.
+
+The database config is used by your application models, Leaf DB and Leaf Auth to connect to the database.
+
+## Paths config
+
+The paths config allows you to configure the paths to your application's folders. The default config looks like this:
+
+```php
+return [
+ 'controllersPath' => 'app/controllers',
+
+ 'modelsPath' => 'app/models',
+
+ 'migrationsPath' => 'app/database/migrations',
+
+ 'seedsPath' => 'app/database/seeds',
+
+ 'factoriesPath' => 'app/database/factories',
+
+ 'helpersPath' => 'app/helpers',
+
+ 'viewsPath' => 'app/views',
+
+ 'configPath' => 'config',
+
+ 'storagePath' => 'storage',
+
+ 'commandsPath' => 'app/console',
+
+ 'routesPath' => 'app/routes',
+
+ 'libPath' => 'lib',
+
+ 'publicPath' => 'public',
+
+ 'databaseStoragePath' => 'storage/app/db'
+];
+```
+
+The paths config is used by Leaf to locate your application's folders. If you want to change the default paths, you can do so in the `config/paths.php` file.
+
+## View Config
+
+The view config allows you to configure the view engine and the view cache. The default config looks like this:
+
+```php
+use Leaf\View;
+
+return [
+ /*
+ |--------------------------------------------------------------------------
+ | Template Engine [EXPERIMENTAL]
+ |--------------------------------------------------------------------------
+ |
+ | Leaf MVC unlike other frameworks tries to give you as much control as
+ | you need. As such, you can decide which view engine to use.
+ |
+ */
+ 'view_engine' => \Leaf\Blade::class,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom config method
+ |--------------------------------------------------------------------------
+ |
+ | Configuration for your templating engine.
+ |
+ */
+ 'config' => function ($config) {
+ View::blade()->config($config['views_path'], $config['cache_path']);
+ },
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom render method
+ |--------------------------------------------------------------------------
+ |
+ | This render method is triggered whenever render() is called
+ | in your app if you're using a custom view engine.
+ |
+ */
+ 'render' => null,
+];
+```
+
+## Next Steps
+
+Follow along with the next steps to learn more about Leaf MVC.
+
+
diff --git a/apps/docs/src/docs/mvc/console.md b/apps/docs/src/docs/mvc/console.md
new file mode 100644
index 0000000..f4e4a82
--- /dev/null
+++ b/apps/docs/src/docs/mvc/console.md
@@ -0,0 +1,460 @@
+# MVC Console
+
+
+
+Leaf MVC and Leaf API come with a robust console tool to help you manage your app from the command line. We call this tool Aloe.
+
+Aloe comes with a predefined set of commands which provide project scaffolding, database and app management right from your terminal. To view all available commands, run `php leaf list` from your terminal.
+
+## Aloe CLI vs Leaf CLI
+
+Before we go any further, it's important to note that Aloe is not the same as Leaf CLI.
+
+Leaf CLI is a tool for creating and managing your Leaf applications, it is installed globally and can be used in Leaf and Leaf MVC/Leaf API applications. The Leaf CLI has a more generalized set of commands which can be used in any Leaf application.
+
+Aloe on the other hand is a tool for managing Leaf MVC and Leaf API applications. It is limited to the root directory of your Leaf MVC/Leaf API application and has a more specific set of commands which are only available in Leaf MVC/Leaf API applications.
+
+## Aloe Commands
+
+Aloe provides a large set of commands to ease your development process. Although they may look like a lot, we can group them into 6 categories:
+
+- App commands
+- Scaffold commands
+- Generate commands
+- Delete commands
+- Database commands
+- View commands
+
+## App Commands
+
+App commands are commands which help you manage your Leaf MVC/Leaf API application. These commands are used to manage your app's state, interact with your app and it's dependencies and more.
+
+### Serve
+
+The `serve` command is used to start the Leaf development server. It is the same as running `php -S localhost:[PORT]` from your terminal, but it also runs your app's bootstrapping process.
+
+To start the Leaf development server, run `php leaf serve` from your terminal. You can also specify a port to run the server on by running `php leaf serve --port=[PORT]`.
+
+```bash
+php leaf serve
+```
+
+### Interact
+
+The `interact` command is used to start the Leaf interactive shell. The interactive shell is a REPL (Read-Eval-Print-Loop) which allows you to interact with your app from the command line. It is powered by [PsySH](https://psysh.org/).
+
+```bash
+php leaf interact
+```
+
+### Up and Down
+
+The `app:up` and `app:down` commands are used to put your app in maintenance mode. When your app is in maintenance mode, all requests to your app will return a 503 response. This is useful when you want to take your app down for maintenance.
+
+To put your app in maintenance mode, run `php leaf app:down`. To take your app out of maintenance mode, run `php leaf app:up`.
+
+```bash
+php leaf app:down
+php leaf app:up
+```
+
+## Scaffold Commands
+
+Scaffold commands are commands which help you scaffold your Leaf MVC/Leaf API application. These commands are used to create new files and folders in your app. Some of these commands even scaffold entire flows for you.
+
+### Auth Scaffold
+
+The `auth:scaffold` command is used to scaffold basic authentication for your Leaf MVC/Leaf API application. It creates required models, controllers, views and routes, migrations and everything you need to get started with authentication. Depending on your flow, you may need to make some changes to the files generated by this command.
+
+Note that this command will generate different files depending on whether you're using Leaf MVC or Leaf API.
+
+If you're building a Leaf MVC app, this command will automatically scaffold a login and registration flow for you with views and controllers, however, since Leaf API comes with the View layer disabled, this command will only scaffold the required models, controllers, routes and migrations for you. All controllers in Leaf API will return JSON responses.
+
+You can force the command to generate files for Leaf MVC or Leaf API by passing the `--mvc` or `--api` flag respectively.
+
+```bash
+php leaf auth:scaffold
+php leaf auth:scaffold --mvc # force Leaf MVC
+php leaf auth:scaffold --api # force Leaf API
+```
+
+You can find the Leaf MVC authentication documentation [here](/modules/auth/mvc).
+
+### Mail Setup
+
+The `mail:setup` command is used to setup mailing in your Leaf MVC/Leaf API application. It installs the Leaf Mail package and creates a `mail.php` config file in your `config` folder as well as a demo mailer in your `app/mailers` folder.
+
+```bash
+php leaf mail:setup
+```
+
+Find the MVC Mail documentation [here](/docs/mvc/mail).
+
+### Env Generate
+
+The `env:generate` command is used to generate a `.env` file in your Leaf MVC/Leaf API application. It generates a `.env` file from your `.env.example` file and fills it with the values in your `.env.example` file.
+
+If there is no `.env.example` file in your app, this command will generate a `.env` file from scratch.
+
+```bash
+php leaf env:generate
+```
+
+### Devtools Install
+
+The `devtools:install` command is used to install and setup the Leaf PHP devtools in your Leaf MVC/Leaf API application.
+
+```bash
+php leaf devtools:install
+```
+
+You can find the Leaf PHP devtools documentation [here](/modules/devtools/).
+
+### View Install
+
+The `view:install` command is used to install and setup the frontend of your Leaf MVC/Leaf API application. It can be used to install any frontend framework of your choice, vite, tailwind or a template engine like bare ui. You can use the `--react`, `--vue`, `--blade`, and `--bareui` options to scaffold your frontend setup.
+
+```bash
+leaf view:install --react
+```
+
+You can also use the `--vite` and `--tailwind` options to scaffold Vite and Tailwind respectively.
+
+```bash
+php leaf view:install --vite
+php leaf view:install --tailwind
+```
+
+You can check the [frontend documentation](/modules/views/) for more information.
+
+## Generate Commands
+
+These commands are used to generate files like controllers, models, migrations, views and more directly into your app.
+
+### Generate Command
+
+The `g:command` command is used to generate a new console command in your Leaf MVC/Leaf API application. It creates a new command class in your `app/commands` folder.
+
+```bash
+php leaf g:command [name]
+```
+
+### Generate Controller
+
+The `g:controller` command is used to generate a new controller in your Leaf MVC/Leaf API application. It creates a new controller class in your `app/controllers` folder.
+
+```bash
+php leaf g:controller [name]
+```
+
+This command can also be used to generate a controller with a resource route. To generate a controller with a resource route, pass the `--resource` flag.
+
+```bash
+php leaf g:controller [name] --resource
+```
+
+Resource controllers are controllers which have a route for every CRUD operation. You can find more information about resource controllers [here](/docs/mvc/controllers).
+
+You can also generate a controller and a model at the same time by passing the `--model` flag.
+
+```bash
+php leaf g:controller [name] --model
+```
+
+You can also generate a migration and model at the same time by passing the `--all` flag, or just `-a` for short.
+
+```bash
+php leaf g:controller [name] --all
+php leaf g:controller [name] -a
+```
+
+### Generate Factory
+
+The `g:factory` command is used to generate a new model factory in your Leaf MVC/Leaf API application. It creates a new factory class in your `app/database/factories` folder.
+
+```bash
+php leaf g:factory [name]
+```
+
+### Generate Helper
+
+The `g:helper` command is used to generate a new helper class in your Leaf MVC/Leaf API application. It creates a new helper class in your `app/helpers` folder.
+
+```bash
+php leaf g:helper [name]
+```
+
+### Generate Mailer
+
+The `g:mailer` command is used to generate a new mailer in your Leaf MVC/Leaf API application. It creates a new mailer class in your `app/mailers` folder.
+
+```bash
+php leaf g:mailer [name]
+```
+
+### Generate Migration
+
+The `g:migration` command is used to generate a new migration in your Leaf MVC/Leaf API application. It creates a new migration file in your `app/database/migrations` folder.
+
+```bash
+php leaf g:migration [name]
+```
+
+By default, this command assumes your table name from the migration name. For example, if you run `php leaf g:migration users`, the command will assume your table name is `users`. You can override this by passing the `--table` flag.
+
+```bash
+php leaf g:migration [name] --table=[table_name]
+```
+
+### Generate Model
+
+The `g:model` command is used to generate a new model in your Leaf MVC/Leaf API application. It creates a new model class in your `app/models` folder.
+
+```bash
+php leaf g:model [name]
+```
+
+If you want to generate a model with a migration, you can pass the `--migration` flag.
+
+```bash
+php leaf g:model [name] --migration
+```
+
+### Generate Seed
+
+The `g:seed` command is used to generate a new seed file in your Leaf MVC/Leaf API application. It creates a new seed file in your `app/database/seeds` folder.
+
+```bash
+php leaf g:seed [name]
+```
+
+### Generate Template
+
+The `g:template` command is used to generate a new view file in your Leaf MVC/Leaf API application. It creates a new view file in your `resources/views` folder.
+
+```bash
+php leaf g:template [name]
+```
+
+You can tell this command what kind of view file you want to create using the `--type` flag. The available types are `blade`, `jsx`, `vue` and `html`.
+
+```bash
+php leaf g:template [name] --type=blade
+php leaf g:template [name] --type=jsx
+php leaf g:template [name] --type=vue
+php leaf g:template [name] --type=html
+```
+
+## Delete Commands
+
+These commands are used to delete files like controllers, models, migrations, views and more directly from your app.
+
+### Delete Command
+
+The `d:command` command is used to delete a console command from your Leaf MVC/Leaf API application. It deletes a command class from your `app/commands` folder and automatically unregisters it from Aloe.
+
+```bash
+php leaf d:command [name]
+```
+
+### Delete Controller
+
+The `d:controller` command is used to delete a controller from your Leaf MVC/Leaf API application. It deletes a controller class from your `app/controllers` folder.
+
+```bash
+php leaf d:controller [name]
+```
+
+### Delete Factory
+
+The `d:factory` command is used to delete a model factory from your Leaf MVC/Leaf API application. It deletes a factory class from your `app/database/factories` folder.
+
+```bash
+php leaf d:factory [name]
+```
+
+### Delete Migration
+
+The `d:migration` command is used to delete a migration from your Leaf MVC/Leaf API application. It deletes a migration file from your `app/database/migrations` folder.
+
+```bash
+php leaf d:migration [name]
+```
+
+### Delete Model
+
+The `d:model` command is used to delete a model from your Leaf MVC/Leaf API application. It deletes a model class from your `app/models` folder.
+
+```bash
+php leaf d:model [name]
+```
+
+### Delete Seed
+
+The `d:seed` command is used to delete a seed file from your Leaf MVC/Leaf API application. It deletes a seed file from your `app/database/seeds` folder.
+
+```bash
+php leaf d:seed [name]
+```
+
+## Database Commands
+
+These commands are used to manage your database. They are used to create, migrate, seed and rollback your database.
+
+### Install Database
+
+The `db:install` command is used to create a new database from your `.env` file. It creates a new database using the credentials in your `.env` file.
+
+```bash
+php leaf db:install
+```
+
+### Migrate Database
+
+The `db:migrate` command is used to run your database migrations. It runs all migrations in your `app/database/migrations` folder.
+
+```bash
+php leaf db:migrate
+```
+
+You can also run a specific migration by passing the migration name to the `--file` flag.
+
+```bash
+php leaf db:migrate --file=[migration_name]
+php leaf db:migrate --file=users # runs the ..._create_users_table migration
+```
+
+You can also seed your database after running your migrations by passing the `--seed` flag.
+
+```bash
+php leaf db:migrate --seed
+```
+
+### Reset Database
+
+The `db:reset` command is used to rollback, migrate and seed your database. It runs the `db:rollback`, `db:migrate` and `db:seed` commands in that order.
+
+```bash
+php leaf db:reset
+```
+
+If you want to prevent seeding your database, you can pass the `--noSeed` flag.
+
+```bash
+php leaf db:reset --noSeed
+```
+
+### Rollback Database
+
+The `db:rollback` command is used to rollback your database migrations. It rolls back all migrations in your `app/database/migrations` folder.
+
+```bash
+php leaf db:rollback
+```
+
+You can also rollback a specific migration by passing the migration name to the `--file` flag.
+
+```bash
+php leaf db:rollback --file=[migration_name]
+php leaf db:rollback --file=users # rolls back the ..._create_users_table migration
+```
+
+You may also rollback a specific number of migrations by passing the number to the `--step` flag.
+
+```bash
+php leaf db:rollback --step=[number]
+php leaf db:rollback --step=2 # rolls back the last 2 migrations
+```
+
+### Seed Database
+
+The `db:seed` command is used to seed your database. It runs all seed files loading in the `app/database/seeds/DatabaseSeeder.php` file. In this file, you can specify which seed files to run.
+
+```bash
+php leaf db:seed
+```
+
+## View Commands
+
+These commands are used to manage your frontend. They are used to build, serve and install your frontend. We already covered the `view:install` command in the Scaffold Commands section above.
+
+### Build Frontend
+
+The `view:build` command is used to build your frontend. It builds your frontend using the build command specified in your `package.json` file.
+
+```bash
+php leaf view:build
+```
+
+### Serve Frontend
+
+The `view:serve` command is used to serve your frontend. It serves your frontend using the `dev` command specified in your `package.json` file.
+
+```bash
+php leaf view:serve
+```
+
+## Command List
+
+This is a list of every command available in Aloe. To view this list from your terminal, run `php leaf list`.
+
+```bash
+Leaf MVC v3.5.0
+
+Usage:
+ command [options] [arguments]
+
+Options:
+ -h, --help Display help for the given command. When no command is given display help for the list command
+ -q, --quiet Do not output any message
+ -V, --version Display this application version
+ --ansi|--no-ansi Force (or disable --no-ansi) ANSI output
+ -n, --no-interaction Do not ask any interactive question
+ -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
+
+Available commands:
+ completion Dump the shell completion script
+ example example command's description
+ help Display help for a command
+ interact Interact with your application
+ list List commands
+ serve Start the leaf development server
+ app
+ app:down Place app in maintainance mode
+ app:up Remove app from maintainance mode
+ auth
+ auth:scaffold Scaffold basic app authentication
+ d
+ d:command Delete a console command
+ d:controller Delete a controller
+ d:factory Delete a model factory
+ d:migration Delete a migration
+ d:model Delete a model
+ d:seed Delete a model seeder
+ db
+ db:install Create new database from .env variables
+ db:migrate Run the database migrations
+ db:reset Rollback, migrate and seed database
+ db:rollback Rollback all database migrations
+ db:seed Seed the database with records
+ devtools
+ devtools:install Install the Leaf PHP devtools
+ env
+ env:generate Generate .env file
+ g
+ g:command Create a new console command
+ g:controller Create a new controller class
+ g:factory Create a new model factory
+ g:helper Create a new helper class
+ g:mailer Create a new mailer
+ g:migration Create a new migration file
+ g:model Create a new model class
+ g:seed Create a new seed file
+ g:template Create a new view file
+ mail
+ mail:setup Install leaf mail and setup mail config
+ view
+ view:build Run your frontend dev server
+ view:dev [view:serve] Run your frontend dev server
+ view:install Run a script in your composer.json
+```
diff --git a/apps/docs/src/docs/mvc/controllers.md b/apps/docs/src/docs/mvc/controllers.md
new file mode 100644
index 0000000..557fbbc
--- /dev/null
+++ b/apps/docs/src/docs/mvc/controllers.md
@@ -0,0 +1,238 @@
+# Controllers
+
+
+
+Instead of using Closures in route files to define all your request handling logic, you can use controllers to organize this behavior. Controllers can group related request handling logic into a single class. For instance, you may want to group all logic that handles user account details into an `AccountsController` class: actions such as displaying, creating, updating, and deleting users.
+
+Controllers can also be shared among different route files, giving you a single location to define a controller that can be used in different contexts throughout your application. Leaf MVC and Leaf API controllers are stored in the `app/controllers`. Any new controller you create will be saved in this location.
+
+## Generating Controllers
+
+Leaf MVC and Leaf API come with a console helper that can generate a new controller for you. To create a new controller, use the `g:controller` command:
+
+```bash
+php leaf g:controller users
+```
+
+This will create a new `UsersController` class in the `app/controllers` directory. The controller will contain a single method, `index`, that returns a simple string:
+
+```php
+json([
+ 'message' => 'UsersController@index output'
+ ]);
+ }
+}
+```
+
+You can see that the controller extends the `App\Controllers\Controller` class. This is the base controller class provided by Leaf MVC and Leaf API. It is the parent class for all your application's controllers and serves as a place to put shared logic.
+
+## Defining Controllers
+
+The above section looked at generating a new controller using the console helper in Leaf MVC and Leaf API. If you are using Leaf Core, you can manually create a controller in any way you prefer. Controllers are basically classes that have callable actions and return responses. To add some extra functionality to your controllers, you can extend the `Leaf\Controller` class.
+
+For example, let's create a new controller that returns a simple string:
+
+```php
+json('Hello World!');
+ }
+}
+```
+
+You can define a route to this controller action like so:
+
+
+
+## Resource Controllers
+
+Leaf resource routing assigns the typical create, read, update, and delete ("CRUD") routes to a controller with a single line of code. To get started, we can use the `g:controller` command's `--resource` option to quickly create a controller to handle these actions:
+
+```sh
+php leaf g:controller Photos --resource
+```
+
+This command will generate a controller at `app/controllers/PhotosController.php`. The controller will contain a method for each of the available resource operations. Next, you may register a resource route that points to the controller:
+
+```php
+app()->resource('/user/(\d+)', 'UsersController');
+```
+
+The `resource` method accepts a URI and a controller name. The URI may contain route parameters, which will be passed to the controller methods. The controller name should be the fully-qualified class name of the controller. In this example, the `UsersController` class should be defined in the `app/controllers` directory.
+
+This single route declaration creates multiple routes to handle a variety of actions on the resource. The generated controller will already have methods stubbed for each of these actions:
+
+```php
+ -m
+```
+
+Create a template for your controller
+
+```bash
+php leaf g:controller -t
+```
+
+Create a model and migration for your controller
+
+```bash
+php leaf g:controller -a
+```
+
+### Controller Help
+
+```bash
+Description:
+ Create a new controller class
+
+Usage:
+ g:controller [options] [--]
+
+Arguments:
+ controller controller name
+
+Options:
+ -a, --all Create a model and migration for controller
+ -m, --model Create a model for controller
+ -r, --resource Create a resource controller
+ -w, --web Create a web(ordinary) controller
+ -h, --help Display this help message
+ -q, --quiet Do not output any message
+ -V, --version Display this application version
+ --ansi Force ANSI output
+ --no-ansi Disable ANSI output
+ -n, --no-interaction Do not ask any interactive question
+ -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debu
+```
+
+## Next Steps
+
+Follow along with the next steps to learn more about Leaf MVC.
+
+
diff --git a/apps/docs/src/docs/mvc/globals.md b/apps/docs/src/docs/mvc/globals.md
new file mode 100644
index 0000000..6408e67
--- /dev/null
+++ b/apps/docs/src/docs/mvc/globals.md
@@ -0,0 +1,287 @@
+# MVC Helpers
+
+Just as Leaf has a couple of built-in helpers, Leaf MVC also ships with a bunch of helpers to make your life easier. These helpers are available throughout your application and help you with common tasks like finding files and loading paths.
+
+## Loading app paths
+
+Since Leaf MVC and Leaf API come with a robust structure out of the box, they also come with quick ways to reference files in these structures. For example, if you want to reference a file in your `public` folder, you can use the `PublicPath()` helper.
+
+### AppPaths()
+
+This returns an array of all the paths in your application.
+
+```php
+$paths = AppPaths();
+
+$controllersPath = AppPaths('controllers'); // you can do this
+$controllersPath = $paths['controllers']; // or this
+```
+
+If the path you are looking for doesn't have a helper function, you can use the `AppPaths()` helper to get the path. Just make sure that the path is defined in your `config/paths.php` file.
+
+```php
+AppPaths('weirdPath');
+```
+
+### assets()
+
+This returns the path to your assets folder. You can pass in a file name to get the path to that file.
+
+```php
+$asset = assets('css/main.css');
+// -> public/assets/css/main.css
+```
+
+You can configure the path to your assets folder in your `config/paths.php` file.
+
+```php
+'assets' => 'public/assets'
+```
+
+### ConfigPath()
+
+This returns the path to your config folder. You can pass in a file name to get the path to that file.
+
+```php
+$dbConfigFile = ConfigPath('db.php');
+// -> config/db.php
+```
+
+### CommandsPath()
+
+This returns the path to your commands folder. You can pass in a file name to get the path to that file.
+
+```php
+$command = CommandsPath('MainCommand.php');
+// -> app/commands/MainCommand.php
+```
+
+### ControllersPath()
+
+This returns the path to your controllers folder. You can pass in a file name to get the path to that file.
+
+```php
+$controller = ControllersPath('MainController.php');
+// -> app/controllers/MainController.php
+```
+
+### DatabasePath()
+
+This returns the path to your database folder. You can pass in a file name to get the path to that file.
+
+```php
+$database = DatabasePath('migrations');
+// -> app/database/migrations
+```
+
+### FactoriesPath()
+
+This returns the path to your factories folder. You can pass in a file name to get the path to that file.
+
+```php
+$factory = FactoriesPath('UserFactory.php');
+// -> app/database/factories/UserFactory.php
+```
+
+### HelpersPath()
+
+This returns the path to your helpers folder. You can pass in a file name to get the path to that file.
+
+```php
+$helper = HelpersPath('MainHelper.php');
+// -> app/helpers/MainHelper.php
+```
+
+### LibPath()
+
+This returns the path to your lib folder. You can pass in a file name to get the path to that file.
+
+```php
+$lib = LibPath('MainLib.php');
+// -> lib/MainLib.php
+```
+
+### MigrationsPath()
+
+This returns the path to your migrations folder. You can pass in a file name to get the path to that file.
+
+```php
+$migration = MigrationsPath('MainMigration.php');
+// -> app/database/migrations/MainMigration.php
+```
+
+### ModelsPath()
+
+This returns the path to your models folder. You can pass in a file name to get the path to that file.
+
+```php
+$model = ModelsPath('User.php');
+// -> app/models/User.php
+```
+
+### PublicPath()
+
+This returns the path to your public folder. You can pass in a file name to get the path to that file.
+
+```php
+$public = PublicPath('index.php');
+// -> public/index.php
+```
+
+### RoutesPath()
+
+This returns the path to your routes folder. You can pass in a file name to get the path to that file.
+
+```php
+$routes = RoutesPath('_auth.php');
+// -> app/routes/_auth.php
+```
+
+### SeedsPath()
+
+This returns the path to your seeds folder. You can pass in a file name to get the path to that file.
+
+```php
+$seed = SeedsPath('MainSeed.php');
+// -> app/database/seeds/MainSeed.php
+```
+
+### StoragePath()
+
+This returns the path to your storage folder. You can pass in a file name to get the path to that file.
+
+```php
+$storage = StoragePath('MainStorage.php');
+// -> storage/MainStorage.php
+```
+
+### ViewsPath()
+
+This returns the path to your views folder. You can pass in a file name to get the path to that file.
+
+```php
+$view = ViewsPath('index.leaf.php');
+// -> app/views/index.leaf.php
+```
+
+## Loading app config
+
+There are some situations that may require you to load up your config files. For such situations, we've prepared a couple of helpers to help you load up your config files.
+
+### MvcConfig()
+
+This returns an array of all the config files in your application.
+
+```php
+$configs = MvcConfig();
+
+$dbConfig = MvcConfig('db'); // you can do this
+$dbConfig = $configs['db']; // or this
+```
+
+It also allows you to load up a specific config from the config file you pass in.
+
+```php
+$config = MvcConfig('db', 'host'); // you can do this
+$config = $configs['db']['host']; // or this
+```
+
+### AppConfig()
+
+This returns an array of all the config in your `config/app.php` file.
+
+```php
+$configs = AppConfig();
+
+$debug = AppConfig('debug'); // you can do this
+$debug = $configs['debug']; // or this
+```
+
+### AuthConfig()
+
+This returns an array of all the config in your `config/auth.php` file.
+
+```php
+$configs = AuthConfig();
+
+$auth = AuthConfig('auth'); // you can do this
+$auth = $configs['auth']; // or this
+```
+
+### CorsConfig()
+
+This returns an array of all the config in your `config/cors.php` file.
+
+```php
+$configs = CorsConfig();
+
+$origin = CorsConfig('origin'); // you can do this
+$origin = $configs['origin']; // or this
+```
+
+### DatabaseConfig()
+
+This returns an array of all the config in your `config/db.php` file.
+
+```php
+$configs = DatabaseConfig();
+
+$host = DatabaseConfig('host'); // you can do this
+$host = $configs['host']; // or this
+```
+
+### MailConfig()
+
+This returns an array of all the config in your `config/mail.php` file.
+
+```php
+$configs = MailConfig();
+
+$host = MailConfig('host'); // you can do this
+$host = $configs['host']; // or this
+```
+
+### ViewConfig()
+
+This returns an array of all the config in your `config/view.php` file.
+
+```php
+$configs = ViewConfig();
+
+$host = ViewConfig('viewEngine'); // you can do this
+$host = $configs['viewEngine']; // or this
+```
+
+## View Helpers
+
+View helpers are in charge of outputting your views.
+
+### view()
+
+This method calls the `render()` method of whatever view engine you are using. It takes in the name of the view you want to render and an optional array of data you want to pass to the view.
+
+```php
+view('index', [
+ 'name' => 'Leaf'
+]);
+```
+
+The only issue here is that not all view engines directly output the view. For example, Blade and Twig return the view as a string. This means that you have to echo the view.
+
+```php
+echo view('index', [
+ 'name' => 'Leaf'
+]);
+```
+
+To make this easier, Leaf MVC ships with a `render()` method that calls the `view()` helper and echoes the view.
+
+### render()
+
+This method calls the `view()` helper and echoes the view. It takes in the name of the view you want to render and an optional array of data you want to pass to the view.
+
+```php
+render('index', [
+ 'name' => 'Leaf'
+]);
+```
diff --git a/apps/docs/src/docs/mvc/index.md b/apps/docs/src/docs/mvc/index.md
new file mode 100644
index 0000000..42c7e9e
--- /dev/null
+++ b/apps/docs/src/docs/mvc/index.md
@@ -0,0 +1,71 @@
+
+
+# Leaf + MVC
+
+
+
+Leaf is a simple PHP framework/set of libraries that can be used to build any kind of application. By default, Leaf doesn't give you a lot of structure, but it fully supports the MVC pattern without any extra configuration.
+
+## What is MVC?
+
+MVC stands for Model-View-Controller. It is a pattern that separates your application into three distinct parts:
+
+- Models: These are the classes that represent your data. They are responsible for interacting with your database, and for validating your data.
+- Views: These are the files that are responsible for displaying your data to the user. They are usually written in HTML, but can also be written in other templating languages like [BareUI](https://leafphp.dev/modules/views/bareui/) or [Blade](https://leafphp.dev/modules/views/blade/) or frameworks like [Vue](https://vuejs.org/) or [React](https://reactjs.org/)
+- Controllers: These are the classes that are responsible for handling the user's request, and for returning the appropriate response.
+
+
+
+## MVC in Leaf
+
+Leaf out of the box doesn't provide any structure, however, the Leaf team also provides a few setups that you can use to get started with Leaf and MVC. These setups are designed to give you a good starting point for your application, and come with additional tooling that make building with Leaf even faster.
+
+We provide two setups for you to choose from:
+
+- [Leaf MVC](/docs/leafmvc/)
+- [Leaf API](/docs/leafapi/)
+
+### Leaf MVC vs Leaf API
+
+| Engine | Main use | Extra Notes |
+| --------------------------- | :-------------: | :----------------------------: |
+| [Leaf MVC](/docs/leafmvc/) | General purpose | - |
+| [Leaf API](/docs/leafapi/) | Building APIs | View layer disabled by default |
+
+Leaf MVC and Leaf API pretty much support the same tooling, but Leaf API is designed to be used for building APIs. It comes with the view layer disabled by default, and comes with a few extra tools that make building APIs with Leaf even easier.
+
+## Leaf Skeleton
+
+Skeleton was designed to be a simple starting point for your application, and came with just the bare minimum to get you started. However, we have decided to deprecate Skeleton in favor of the Leaf CLI. The Leaf CLI is a command-line tool for generating Leaf projects, installing modules, and more.
+
+We recently released an update to the Leaf CLI that allows you to select specific features you want to include and generate a project with everything you need. This functionality is similar to what Skeleton provided but is more flexible and allows you to create projects with only the features that you need, which is why we've decided to deprecate Leaf Skeleton.
+
+Skeleton will still be available for download, but we won't be updating it anymore. We recommend that you generate a project with the CLI or use Leaf MVC or Leaf API instead.
+
+[> Leaf CLI Docs](/docs/cli/)
+
+## MVC Tools
+
+Besides the MVC setups, Leaf also provides a few tools that can help you build your own MVC setup if you want to. You can check the "MVC Tools" section in the sidebar to learn more about these tools.
+
+
diff --git a/apps/docs/src/docs/mvc/libraries.md b/apps/docs/src/docs/mvc/libraries.md
new file mode 100644
index 0000000..be9c240
--- /dev/null
+++ b/apps/docs/src/docs/mvc/libraries.md
@@ -0,0 +1,81 @@
+# Custom Libraries
+
+Sometimes you might want to write some application logic that doesn't fit into a controller, model, or helpers. It makes sense to create a custom library for this functionality. For example, you might want to create a library that calculates the distance between two points on a map. You could then use this library in any controller, helper, or view. Leaf MVC and Leaf API both come with a `lib` folder where you can store your custom libraries.
+
+Custom libraries are not stored in the `app` folder because they are not part of the application's core functionality. They are more like helpers, however, unlike helpers they can be full classes, functions or just data structures and also require no structured namespace.
+
+## Autoloading Libraries
+
+Leaf MVC and Leaf API will only automatically load your libraries for you if you tell it to do so. You can do this by **uncommenting** the following line in your `public/index.php` file:
+
+```php
+// \Leaf\Core::loadLibs();
+```
+
+Once you do so, you can start creating your own libraries.
+
+## Creating a Library
+
+To create a library, simply create a new file in the `lib` folder. For example, let's create a library called `Math.php`:
+
+```php
+ $sum]);
+ }
+}
+```
+
+## Library Structure
+
+As mentioned above, libraries can be just about anything. They are completely based on your own preference. However, it is recommended that you keep your libraries as simple as possible. Below is the same `Math` library from above, but this time it is a simple function instead of a class:
+
+```php
+ $sum]);
+ }
+}
+```
diff --git a/apps/docs/src/docs/mvc/mail.md b/apps/docs/src/docs/mvc/mail.md
new file mode 100644
index 0000000..6942918
--- /dev/null
+++ b/apps/docs/src/docs/mvc/mail.md
@@ -0,0 +1,352 @@
+# Mailing
+
+Leaf provides amazing support for handling mailing in your application via Leaf Mail. The documentation for the Leaf Mailer module can be found on the [Leaf Mail page](/modules/mail/). This page will only cover how to use Leaf Mail in your Leaf MVC or Leaf API application.
+
+If you haven't already, we recommend you read the [Leaf Mail documentation](/modules/mail/) before continuing.
+
+## Setting up Leaf Mail
+
+Leaf MVC and Leaf API provide a lean setup out of the box that comes without mailing pre-activated. To activate mailing, you can run the following command in the root of your app:
+
+```bash
+php leaf mail:setup
+```
+
+This will setup everything you need to start using mailing and will also create a `mail.php` file in your `config` folder. This file contains all the configuration options for Leaf Mail. You can read more about the configuration options in the [Leaf Mail documentation](/modules/mail/#mailer-config).
+
+## Mail server connection
+
+After setting up Leaf Mail, the next thing to do is to connect to your mail server. This connection is what Leaf Mail will attempt to use whenever you initiate an email send.
+
+Before you can connect to your mail server, you need to have your mail server credentials. You can get these from your mail server provider.
+
+### Your mail provider
+
+If you are using a mail provider like Mailgun, Sendgrid, etc, you need to head over to your provider's dashboard and get your mail server credentials. From there, you need to update the mail host and port in your `.env` file.
+
+```env
+MAIL_HOST=smtp.mailtrap.io
+MAIL_PORT=2525
+```
+
+There are different kinds of server connections you can use. The most common one is SMTP using a username and password. You can also use OAuth authentication with a provider like Google.
+
+### Connection with username and password
+
+To connect to a server using your username and password, you can head over to your `.env` file and update the following values:
+
+```env
+MAIL_USERNAME=null
+MAIL_PASSWORD=null
+```
+
+These are your mail server username and password respectively. **Note that these values are not set by default**. You need to set them yourself.
+
+### Connection with OAuth
+
+Some mail providers like Google require OAuth authentication. To connect to a server using OAuth, you need to add an OAuth provider like league/oauth2-google to your project. You can install this using the Leaf CLI:
+
+```bash
+leaf install league/oauth2-google
+```
+
+Or with composer:
+
+```bash
+composer require league/oauth2-google
+```
+
+From there, you need to update the `config/mail.php` file to work with OAuth instead of username and password. You can do this by replacing these lines:
+
+```php
+'auth' => [
+ 'username' => _env('MAIL_USERNAME'),
+ 'password' => _env('MAIL_PASSWORD'),
+],
+```
+
+With the OAuth config:
+
+```php
+use League\OAuth2\Client\Provider\Google;
+use PHPMailer\PHPMailer\OAuth;
+
+return [
+ ...
+
+ 'auth' => new OAuth(
+ [
+ 'userName' => _env('MAIL_USERNAME'),
+ 'clientSecret' => _env('MAIL_CLIENT_SECRET'),
+ 'clientId' => _env('MAIL_CLIENT_ID'),
+ 'refreshToken' => _env('MAIL_REFRESH_TOKEN'),
+ 'provider' => new Google(
+ [
+ 'clientId' => _env('MAIL_CLIENT_ID'),
+ 'clientSecret' => _env('MAIL_CLIENT_SECRET'),
+ ]
+ ),
+ ]
+ ),
+
+ ...
+];
+```
+
+Since more environment variables have been added, you need to update your `.env` file to include these variables:
+
+```env
+MAIL_CLIENT_ID=
+MAIL_CLIENT_SECRET=
+MAIL_REFRESH_TOKEN=
+```
+
+::: tip .env.example
+It is a good practice to add these variables to your `.env.example` file so that other developers can easily know what environment variables they need to set.
+:::
+
+## Sending emails
+
+Before you can send emails, you need to create a mailer. You can create a mailer by running the following command in the root of your app:
+
+```bash
+php leaf mail:create
+```
+
+A mailer is a class that allows you to group related emails together. Mailers have methods called "actions" which are responsible for handling a specific type of email. For instance, you can create a mailer called `UserMailer` that has a `welcome` action for sending welcome emails, a `passwordReset` action for sending password reset emails, etc.
+
+Using this example, we can create a `UserMailer` by running the following command:
+
+```bash
+php leaf mail:create user
+```
+
+This will create a `UserMailer` class in your `app/mailers` folder. The `UserMailer` class will look like this:
+
+```php
+ 'UserMailer Test',
+ 'body' => 'This is a test mail from action',
+ 'recipientEmail' => $user->email,
+ 'recipientName' => $user->name,
+
+ // next couple of lines can be skipped if you
+ // set defaults in the config/mail.php file
+ 'senderName' => _env('MAIL_SENDER_NAME'),
+ 'senderEmail' => _env('MAIL_SENDER_EMAIL'),
+ ]);
+ }
+}
+```
+
+You can rename the `action` method to anything you want, in this case we will rename it to `welcome`. The method is responsible for creating and returning a new `Mail` instance. The `Mail` instance is what is used to send the email. You can read more about the `Mail` class in the [Leaf Mail documentation](/modules/mail/#mailer-config).
+
+```php
+ 'Welcome to my app',
+ 'body' => 'This is a test mail from action',
+ 'recipientEmail' => $user->email,
+ 'recipientName' => $user->name,
+ ]);
+ }
+}
+```
+
+To send the welcome email, you can call the `welcome` method on the `UserMailer` class like this:
+
+```php
+UserMailer::welcome($user)->send();
+```
+
+## Setting mail defaults
+
+Some values like the `senderName` and `senderEmail` are repeated in every email you send although they may not change. To avoid repeating these values, we set them as defaults in the `config/mail.php` file. You can configure these in your `.env` file like this:
+
+```env
+MAIL_SENDER_NAME=
+MAIL_SENDER_EMAIL=
+MAIL_REPLY_TO_NAME=
+MAIL_REPLY_TO_EMAIL=
+```
+
+### Removing defaults
+
+If you don't want to set defaults, you can remove them from the `config/mail.php` file. The `.env` values will then be ignored. Your updated `config/mail.php` file will look like this:
+
+```php
+...
+'defaults' => [],
+...
+```
+
+Removing these defaults means that you will have to set these values in every email you send.
+
+## Mail Debugging
+
+Leaf Mail by default reports all errors from your mail server. This is useful for debugging, but can be annoying when you are in production. You can disable this by setting the `MAIL_DEBUG` environment variable to `false` in your `.env` file.
+
+```env
+MAIL_DEBUG=false
+```
+
+## Mail Config
+
+The `config/mail.php` file contains all the configuration options for Leaf Mail. You can read more about the configuration options in the [Leaf Mail documentation](/modules/mail/#mailer-config).
+
+```php
+ _env('MAIL_DRIVER', 'smtp'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Mailer hostname
+ |--------------------------------------------------------------------------
+ |
+ | This is the hostname for your mailer
+ |
+ */
+ 'host' => _env('MAIL_HOST', 'smtp.mailtrap.io'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Mailer port
+ |--------------------------------------------------------------------------
+ |
+ | Port to use for mailer connection
+ |
+ */
+ 'port' => _env('MAIL_PORT', 2525),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Keep Alive
+ |--------------------------------------------------------------------------
+ |
+ | This config is used to keep the connection to your mail server alive.
+ | This is useful if you are sending multiple emails. It takes in a boolean.
+ |
+ */
+ 'keepAlive' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Mailer Debug
+ |--------------------------------------------------------------------------
+ |
+ | Enable or disable debug mode. Supported values are:
+ | 'SERVER', false or any value supported by PHPMailer's
+ | SMTPDebug config
+ |
+ */
+ 'debug' => _env('MAIL_DEBUG', 'SERVER'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Mailer Encryption
+ |--------------------------------------------------------------------------
+ |
+ | This is the encryption used for your mailer. Supported values are:
+ | 'STARTTLS' or any value supported by PHPMailer's SMTPSecure config
+ |
+ */
+ 'security' => _env('MAIL_ENCRYPTION', 'STARTTLS'),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Auth
+ |--------------------------------------------------------------------------
+ |
+ | This config handles the authentication details for your mailer.
+ | It supports authentication with username and password and also
+ | OAuth authentication.
+ |
+ | For OAuth authentication, you will need to add an OAuth
+ | provider like league/oauth2-google to your project.
+ |
+ | An example OAuth config is shown below:
+ |
+ | use League\OAuth2\Client\Provider\Google;
+ | use PHPMailer\PHPMailer\OAuth;
+ |
+ | 'auth' => new OAuth(
+ | [
+ | 'userName' => 'mail@gmail.com',
+ | 'clientSecret' => 'CLIENT_SECRET',
+ | 'clientId' => 'CLIENT_ID',
+ | 'refreshToken' => 'GMAIL_REFRESH_TOKEN',
+ | 'provider' => new Google(
+ | [
+ | 'clientId' => 'CLIENT_ID',
+ | 'clientSecret' => 'CLIENT_SECRET',
+ | ]
+ | ),
+ | ]
+ |)
+ */
+ 'auth' => [
+ 'username' => _env('MAIL_USERNAME'),
+ 'password' => _env('MAIL_PASSWORD'),
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Default addresses
+ |--------------------------------------------------------------------------
+ |
+ | This config is used to set default values for the
+ | `recipientEmail`, `recipientName`,
+ | `senderEmail`, `senderName`,
+ | `replyToName`, and `replyToEmail` of your emails.
+ |
+ */
+ 'defaults' => [
+ 'senderName' => _env('MAIL_SENDER_NAME'),
+ 'senderEmail' => _env('MAIL_SENDER_EMAIL'),
+ 'replyToName' => _env('MAIL_REPLY_TO_NAME'),
+ 'replyToEmail' => _env('MAIL_REPLY_TO_EMAIL'),
+ ],
+];
+```
diff --git a/apps/docs/src/docs/mvc/migrations.md b/apps/docs/src/docs/mvc/migrations.md
new file mode 100644
index 0000000..d977f1a
--- /dev/null
+++ b/apps/docs/src/docs/mvc/migrations.md
@@ -0,0 +1,149 @@
+# Migrations
+
+
+
+Database migrations are a technique used in software development to manage changes to a database schema over time. A database schema is the structure of a database that defines the tables, columns, relationships, and constraints that make up the data model.
+
+When changes are made to the database schema, such as adding a new table or column, modifying an existing column's data type, or changing a relationship between tables, database migrations allow developers to update the schema and propagate those changes to all instances of the database.
+
+## Generating Migrations
+
+You can quickly generate migrations using the `g:migration` command:
+
+```bash
+php leaf g:migration
+
+# example
+php leaf g:migration flights
+```
+
+The new migration will be placed in your `app/database/migrations` directory. Each migration file name begins with a timestamp.
+
+## Migration Structure
+
+A migration class contains two methods: up and down. The up method is used to add new tables, columns, or indexes to your database, while the down method should reverse the operations performed by the up method.
+
+You can create and modify tables in the both of these methods. In this example, we create a posts table:
+
+```php
+capsule::schema()->hasTable("posts")):
+ $this->capsule::schema()->create("posts", function (Blueprint $table) {
+ $table->increments('id');
+ $table->string('author_id');
+ $table->string('title');
+ $table->text('body');
+ $table->timestamps();
+ });
+ endif;
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ $this->capsule::schema()->dropIfExists("posts");
+ }
+}
+```
+
+::: tip Note
+Instead of building your migrations from scratch, you can use Leaf's schema builder to generate migrations from JSON data. [Learn more](/docs/mvc/schema).
+:::
+
+## Running migrations
+
+To run all of your outstanding migrations, execute the `db:migrate` command:
+
+```bash
+php leaf db:migrate
+```
+
+You may also run seeds alongside your migrations if you wish to do so:
+
+```bash
+php leaf db:migrate -s
+# or
+php leaf db:migrate --seed
+```
+
+You can also choose to run a specific migration file:
+
+```bash
+php leaf db:migrate -f users
+```
+
+## Rolling Back Migrations
+
+To roll back the latest migration operation, you may use the `db:rollback` command.
+
+```bash
+php leaf db:rollback
+```
+
+You may roll back a limited number of migrations by providing the `step` option to the `rollback` command. For example, the following command will roll back the last five migrations:
+
+```bash
+php leaf db:rollback -s 2
+```
+
+To roll back all migrations, you can just pass `all` as the `step` option.
+
+```bash
+php leaf db:rollback --step all
+```
+
+You can also rollback a specific migration file:
+
+```bash
+php leaf db:rollback -f users
+```
+
+## Refreshing Migrations
+
+If you would like to reset your database and re-run all of your migrations with seeds, you may use the `db:reset` command. This command will drop all tables in your database and re-run all of your migrations:
+
+```bash
+php leaf db:reset
+```
+
+If you want to prevent seeds from running, you can use the `--noSeed` option:
+
+```bash
+php leaf db:reset --noSeed
+```
+
+## Next Steps
+
+Follow along with the next steps to learn more about Leaf MVC.
+
+
diff --git a/apps/docs/src/docs/mvc/models.md b/apps/docs/src/docs/mvc/models.md
new file mode 100644
index 0000000..4094907
--- /dev/null
+++ b/apps/docs/src/docs/mvc/models.md
@@ -0,0 +1,281 @@
+# Models
+
+
+
+A model is a class that represents your application's data and business logic, and it is responsible for managing data storage, retrieval, and manipulation. The Model communicates with the View and the Controller to provide data and to update the application's state.
+
+Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table.
+
+::: tip Base Model
+Leaf Models are built on top of [Laravel's Eloquent ORM](https://laravel.com/docs/10.x/eloquent). This means that you can use all the features of Eloquent in your Leaf applications. You can find the documentation for Eloquent [here](https://laravel.com/docs/10.x/eloquent).
+:::
+
+## Defining Models
+
+Your models can be found in the `app/models` directory in Leaf MVC and Leaf API. Your models can be created using the `php leaf g:model` command from the root of your project.
+
+```bash
+php leaf g:model User
+```
+
+This will create a `User.php` file in the `app/models` directory.
+
+## Model Structure
+
+Leaf MVC and Leaf API come with a base model class. This is to give you a place to configure all your models seamlessly without having access to the `Leaf\Model` file. It also allows you to add logic that you want to be available to all your models.
+
+```php
+class Flight extends Model
+{
+ //
+}
+```
+
+## Leaf Model Conventions
+
+Now, let's look at an example `Flight` model, which we will use to retrieve and store information from our `flights` database table:
+
+```php
+class Flight extends Model
+{
+ //
+}
+```
+
+### Table Names
+
+Note that we did not tell Leaf which table to use for our `Flight` model. By convention, the "snake case", plural name of the class will be used as the table name unless another name is explicitly specified. So, in this case, Leaf will assume the `Flight` model stores records in the `flights` table. You may specify a custom `table` by defining a table property on your model:
+
+```php
+class Flight extends Model
+{
+ /**
+ * The table associated with the model.
+ *
+ * @var string
+ */
+ protected $table = 'my_flights';
+}
+```
+
+### Primary Keys
+
+Leaf will also assume that each table has a primary key column named id. You may define a protected `$primaryKey` property to override this convention:
+
+```php
+class Flight extends Model
+{
+ /**
+ * The primary key associated with the table.
+ *
+ * @var string
+ */
+ protected $primaryKey = 'flight_id';
+}
+```
+
+In addition, Leaf assumes that the primary key is an incrementing integer value, which means that by default the primary key will automatically be cast to an int. If you wish to use a non-incrementing or a non-numeric primary key you must set the public `$incrementing` property on your model to false:
+
+```php
+/**
+* Indicates if the IDs are auto-incrementing.
+*
+* @var bool
+*/
+public $incrementing = false;
+```
+
+If your primary key is not an integer, you should set the protected `$keyType` property on your model to string:
+
+```php
+/**
+* The "type" of the auto-incrementing ID.
+*
+* @var string
+*/
+protected $keyType = 'string';
+```
+
+### Timestamps
+
+By default, Leaf expects `created_at` and `updated_at` columns to exist on your tables. If you do not wish to have these columns automatically managed by Leaf, set the $timestamps property on your model to false:
+
+```php
+class Flight extends Model
+{
+ /**
+ * Indicates if the model should be timestamped.
+ *
+ * @var bool
+ */
+ public $timestamps = false;
+}
+```
+
+If you need to customize the format of your timestamps, set the `$dateFormat` property on your model. This property determines how date attributes are stored in the database, as well as their format when the model is serialized to an array or JSON:
+
+```php
+/**
+* The storage format of the model's date columns.
+*
+* @var string
+*/
+protected $dateFormat = 'U';
+```
+
+If you need to customize the names of the columns used to store the timestamps, you may set the `CREATED_AT` and `UPDATED_AT` constants in your model:
+
+```php
+class Flight extends Model
+{
+ const CREATED_AT = 'creation_date';
+ const UPDATED_AT = 'last_update';
+}
+```
+
+### Database Connection
+
+By default, all Leaf models will use the default database connection configured for your application. If you would like to specify a different connection for the model, use the `$connection` property:
+
+```php
+class Flight extends Model
+{
+ /**
+ * The connection name for the model.
+ *
+ * @var string
+ */
+ protected $connection = 'connection-name';
+}
+```
+
+## Default Attribute Values
+
+If you would like to define the default values for some of your model's attributes, you may define an $attributes property on your model:
+
+```php
+ false,
+ ];
+}
+```
+
+## Retrieving Models
+
+Once you have created a model and its associated database table, you are ready to start retrieving data from your database. Think of each Leaf model as a powerful query builder allowing you to fluently query the database table associated with the model. For example:
+
+```php
+name;
+}
+```
+
+### Adding Additional Constraints
+
+The Leaf all method will return all of the results in the model's table. Since each Leaf model serves as a query builder, you may also add constraints to queries, and then use the get method to retrieve the results:
+
+```php
+$flights = Flight::where('active', 1)->orderBy('name', 'desc')->take(10)->get();
+```
+
+> **You can check [here](https://laravel.com/docs/10.x/queries) for available queries on your models.**
+
+### Refreshing Models
+
+You can refresh models using the `fresh` and `refresh` methods. The `fresh` method will re-retrieve the model from the database. The existing model instance will not be affected:
+
+```php
+$flight = Flight::where('number', 'FR 900')->first();
+
+$freshFlight = $flight->fresh();
+```
+
+The `refresh` method will re-hydrate the existing model using fresh data from the database. In addition, all of its loaded relationships will be refreshed as well:
+
+```php
+$flight = Flight::where('number', 'FR 900')->first();
+
+$flight->number = 'FR 456';
+
+$flight->refresh();
+
+$flight->number; // "FR 900"
+```
+
+## Inserting & Updating Models
+
+### Inserts
+
+To create a new record in the database, create a new model instance, set attributes on the model, then call the save method:
+
+```php
+name = $this->request->name;
+
+ $flight->save();
+ }
+}
+```
+
+In this example, we assign the name parameter from the incoming HTTP request to the name attribute of the `Flight` model instance. When we call the save method, a record will be inserted into the database. The created_at and updated_at timestamps will automatically be set when the save method is called, so there is no need to set them manually.
+
+### Updates
+
+The save method may also be used to update models that already exist in the database. To update a model, you should retrieve it, set any attributes you wish to update, and then call the save method. Again, the updated_at timestamp will automatically be updated, so there is no need to manually set its value:
+
+```php
+$flight = Flight::find(1);
+
+$flight->name = 'New Flight Name';
+
+$flight->save();
+```
+
+Since Leaf Models use Eloquent, you can read more [here](https://laravel.com/docs/10.x/eloquent) to view available methods on the Eloquent object.
+
+## Next Steps
+
+Follow along with the next steps to learn more about Leaf MVC.
+
+
diff --git a/apps/docs/src/docs/mvc/routing.md b/apps/docs/src/docs/mvc/routing.md
new file mode 100644
index 0000000..036d61c
--- /dev/null
+++ b/apps/docs/src/docs/mvc/routing.md
@@ -0,0 +1,101 @@
+# Routing
+
+
+
+Leaf MVC, Leaf API and Skeleton all rely on Leaf's powerful routing engine. The only difference is that routing the MVC way makes use of controllers instead of callable functions. Everything else works the same way. This document will cover the basics of Leaf MVC routing.
+
+::: tip Leaf Routing
+If you're not familiar with Leaf's routing, you should read the [Leaf Routing](/docs/routing/) document first.
+:::
+
+## The Routes Folder
+
+Leaf MVC, Leaf API and Skeleton all have a `routes` folder in which all your application's routes are defined. The `routes` folder is located in the `app` directory in Leaf MVC and Leaf API but is located in the root directory in Skeleton. The `routes` folder contains a single file called `index.php` which is where all your application's routes are defined. This file is automatically loaded by Leaf when your application starts.
+
+## Linking Controllers
+
+Controllers provide a way to organize your application's logic. They are a great way to keep your routes file clean and easy to read. Controllers are just classes that implement the `App\Controllers\Controller` protocol. You can find more information about controllers in the [Controllers](/docs/mvc/controllers) document. This document will focus on how to link controllers to routes.
+
+Leaf provides a simple interface for interacting with controllers and their methods from inside your routes. The idea is to run a method inside your controller whenever a route is matched. This is done by telling Leaf which controller to use and which method to run. Leaf will then create an instance of the controller and run the method.
+
+### Example
+
+Let's say you have a controller called `App\Controllers\HomeController` and you want to run the `index` method inside it whenever the `/` route is matched. You can do this by passing a string containing your controller name and method to your route. The string should be in the format `controllerName@methodName`.
+
+
+
+## Controller Namespaces
+
+In case you're using an auto loader or using leaf in another framework and you have your controllers in another directory, you can do sommething like this
+
+
+
+But this gets tedious if you have a lot of routes. So Leaf allows you to set a "general" namespace, you can set the default namespace to use on your router instance via `setNamespace()`
+
+
+
+This has already been setup for you in the root routes file of your MVC application.
+
+## Next Steps
+
+Follow along with the next steps to learn more about Leaf MVC.
+
+
diff --git a/apps/docs/src/docs/mvc/schema.md b/apps/docs/src/docs/mvc/schema.md
new file mode 100644
index 0000000..52e9149
--- /dev/null
+++ b/apps/docs/src/docs/mvc/schema.md
@@ -0,0 +1,260 @@
+# Schema
+
+
+
+Schema is a simple, yet powerful tool for generating database migrations from JSON data. Instead of dealing with the stress of writing your database migrations from scratch and thinking about all the types of your data, you can simply create a JSON file with sample data and let Leaf do the rest.
+
+## Writing your schema
+
+Schema can be found in the `app/database/schema` folder. To get started, create a new JSON file in the the schemas directory. You can name it anything you want, but it's best to name it after the table you're creating as that is what Leaf will expect unless you specify otherwise.
+
+We can start off by creating a `users.json` file. All that this file should contain is an example of what your data should look like. For example:
+
+```json
+{
+ "id": 1,
+ "username?": "username",
+ "name": "Full Name",
+ "created_at": "",
+ "updated_at": ""
+}
+```
+
+## Using your schema
+
+To use your schema, you can call `Leaf\Schema::build` in your migration. It takes in the name of the schema file to build your migrations with.
+
+```php{12}
+...
+use Leaf\Schema;
+
+class CreateUsers extends Database {
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::build("users");
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ $this->capsule::schema()->dropIfExists("users");
+ }
+}
+```
+
+In the example above, the `users` schema will be used to generate the migration. This means that the `users` table will be created in your database with the fields specified in the schema. To actually run the migration, you can use the `db:migrate` command.
+
+```bash
+php leaf db:migrate
+```
+
+Read more about [migrations](/docs/mvc/migrations).
+
+## Data Types
+
+Leaf Schema is flexible and allows you to specify the type of data you want to store in your database. For the most part, Leaf Schema will automatically detect the type of data you want to store, but you can also specify the type of data you want to store.
+
+### Automatic Types
+
+Leaf Schema will automatically detect the type of data you want to store in your database. For example, if you want to store a string in your database, you can simply specify the string in your schema.
+
+```json
+{
+ ...
+ "username": "username"
+}
+```
+
+In the example above, the `username` field will be set to `$table->string` in the migration. This is the same as using `$table->string('username')` in your migration.
+
+Automatic types are supported for the following types of data:
+
+- string
+- integer
+- boolean
+- float
+- array (will be converted to `enum` in the migration)
+- json (should be a stringified json object)
+
+### Manually Adding Types
+
+Leaf Schema supports all the types supported by Laravel's Schema Builder. You can specify the type of data you want to store in your database by using the type as the value of the field.
+
+```json
+{
+ ...
+ "username:text": "username"
+}
+```
+
+In the example above, the `username` field will be set to `$table->text` in the migration. This is the same as using `$table->text('username')` in your migration.
+
+
+
+## Nullable Fields
+
+If you want to specify that a field should be nullable, you can use the `?` symbol after the field name. This is the same as using `$table->nullable()` in your migration.
+
+```json
+{
+ ...
+ "username?": "username"
+}
+```
+
+### Nullable + Types
+
+If you want to specify that a field should be nullable and also specify the type of data you want to store, you can use the `?` symbol after the field name and also specify the type of data you want to store after the `?` symbol, using `:`.
+
+```json
+{
+ ...
+ "username?:string": "username"
+}
+```
+
+## `id`
+
+The `id` type is used to specify that the field is an auto-incrementing primary key. This is the same as using `$table->bigIncrements('id')` in your migration. Setting a field to `id` will automatically set the field to be an auto-incrementing primary key.
+
+```json
+{
+ "id": 1,
+ ...
+}
+```
+
+If you want to set a field to be an auto-incrementing primary key, but you don't want to set the field to `id`, you can use the `id` type in the key of the field using `:`.
+
+```json
+{
+ "user_id : id": 1,
+ ...
+}
+```
+
+In the example above, the `user_id` field will be set to `$table->bigIncrements` in the migration. This is the same as using `$table->bigIncrements('user_id')` in your migration. Just as with the `id` type, Leaf Schema allows you to specify the name of the field with the type using `:`.
+
+**The spaces around the `:` are optional, so you can also use `user_id:id`.**
+
+## `timestamps`
+
+The `timestamps` type is used to specify that the table should have `created_at` and `updated_at` fields. This is the same as using `$table->timestamps()` in your migration.
+
+```json
+{
+ ...
+ "timestamps": ""
+}
+```
+
+## Foreign Keys
+
+The `*` type is used to specify that the field should be a foreign key. This is the same as using `$table->foreignId` in your migration.
+
+```json
+{
+ ...
+ "user_id*": 1
+}
+```
+
+## Soft Deletes
+
+To specify that a table should have soft deletes, you can use the `softDeletes` key. This is the same as using `$table->softDeletes()` in your migration.
+
+```json
+{
+...
+"softDeletes": ""
+}
+```
+
+## Remember Tokens
+
+To specify that a table should have a remember token, you can use the `rememberToken` key. This is the same as using `$table->rememberToken()` in your migration.
+
+```json
+{
+ ...
+ "rememberToken": ""
+}
+```
diff --git a/apps/docs/src/docs/mvc/seeds.md b/apps/docs/src/docs/mvc/seeds.md
new file mode 100644
index 0000000..d19292e
--- /dev/null
+++ b/apps/docs/src/docs/mvc/seeds.md
@@ -0,0 +1,120 @@
+# DB Seeders
+
+
+
+Database seeds are a way to populate a database with initial data. This initial data can be used to set up default values or pre-populate a database with test data. Database seeds typically contain small amounts of data, such as default settings, test data, or sample records.
+
+Seeds are often used in conjunction with database migrations. After the database schema has been updated or modified, seeds can be used to populate the new or modified tables with initial data. This can be especially useful for testing and development, as it allows developers to work with a pre-populated database without having to manually enter test data.
+
+## Defining Seeds
+
+Seeds can be found in the `app/database/seeds` directory. Each class should be defined in its own file and should extend the `Illuminate\Database\Seeder` class. The `run` method should contain the code that populates the database with data.
+
+Creating this manually is a bit tedious, so you can use the `g:seed` command to generate a new seed class:
+
+```bash
+php leaf g:seed
+
+# example
+php leaf g:seed Users
+```
+
+The new seed class will be placed in your `app/database/seeds` directory. The class name will be the same as the file name, with the first letter capitalized with Seeder appended to the end if it is not already there. Let's take a look at an example:
+
+```php
+name = 'Mychi';
+ $user->email = "mickdd22@gmail.com";
+ $user->password = password_hash("password.demo", PASSWORD_DEFAULT);
+ $user->save();
+ }
+}
+```
+
+## Running Seeds
+
+To run your database seeds, you first need to make sure that your seeds are registered in the `DatabaseSeeder` class. This class is located in the `app/database/seeds` directory. By default, this class contains a `run` method that will execute all of the seed classes that are returned from it. You may add additional seed classes to this property as needed.
+
+```php
+/**
+ * Seed the application's database.
+ *
+ * @return void
+ */
+public function run() : array
+{
+ return [
+ UsersSeeder::class,
+ PostSeeder::class,
+ CommentSeeder::class,
+ ];
+}
+```
+
+Once you have added your seed classes to the `DatabaseSeeder` class, you may run the `db:seed` command to execute the seeds:
+
+```bash
+php leaf db:seed
+```
+
+## Using Factories
+
+Manually specifying the attributes for each model seed is cumbersome. Instead, you can use factories to conveniently generate large amounts of database records. First, review the [factory documentation](/modules/mvc-core/factories) to learn how to define your factories.
+
+This example below uses the defined factory to create 30 random users:
+
+```php
+create(30)->save();
+ }
+}
+```
+
+## Next Steps
+
+Follow along with the next steps to learn more about Leaf MVC.
+
+
diff --git a/apps/docs/src/docs/mvc/views.md b/apps/docs/src/docs/mvc/views.md
new file mode 100644
index 0000000..a3364a0
--- /dev/null
+++ b/apps/docs/src/docs/mvc/views.md
@@ -0,0 +1,81 @@
+# Views
+
+
+
+Views make up the 'V' in MVC. Views allow you to separate your logic from your presentation layer instead of mixing them together in a single file. This allows you to easily change the look and feel of your application without having to change any of your logic.
+
+## View Engines
+
+Leaf comes with support for 3 view engines designed by the team at Leaf:
+
+| Engine | Use case |
+| -------------------------------- | -------------------------------------------- |
+| [bareui](/modules/views/bareui/) | Blazing fast templating with no compile time |
+| [veins](/modules/views/veins/) | Lightweight but powerful templating engine |
+| [blade](/modules/views/blade/) | Laravel blade templating engine for leaf |
+
+Leaf MVC and Leaf API come with Blade already installed and configured, but of course, you can use any templating engine you prefer. These have first party support, and work amazingly well out of the box.
+
+***You can find more information on the [Views Docs Page](/modules/views/)***
+
+## Defining Views
+
+Views are defined in the `app/views` directory in Leaf API and Leaf MVC. You can create subdirectories to organize your views based on your preference. For example, you might create a `layouts` directory to store your layout files. To quickly create a view, you can use the `php leaf g:template` command from the root of your project.
+
+```bash
+php leaf g:template home
+```
+
+This will create a file called `home.blade.php` in the `app/views` directory.
+
+## Rendering Views
+
+Leaf ships a `view` method as an extension of functional mode. This method allows you to render a view/template found in the views directory. This method accepts two parameters:
+
+- The name of the view to render
+- Data to pass to the view
+
+```php
+echo view('home', ['name' => 'John Doe']);
+```
+
+Notice that we pass the name of the view without the file extension. This is because Leaf will automatically append the correct file extension based on the view engine you're using.
+
+### The `render()` method
+
+To make things even easier for you, Leaf also ships with a `render()` method. This method accepts the same parameters as the `view()` method but automatically outputs the views with the correct headers in place.
+
+```php
+render('home', ['name' => 'John Doe']);
+```
+
+## Asset Bundling
+
+[Vite](https://vitejs.dev/) is a modern build tool for frontend applications. It aims to provide a faster and leaner development experience for modern web projects. Leaf allows you to bundle your CSS and JS assets using vite, using the powerful [leaf-vite](/modules/views/vite/) module.
+
+[> Read the docs](/modules/views/vite/)
+
+## Frontend Frameworks
+
+Leaf has support for some of the most popular frontend frameworks using [Inertia.js](https://inertiajs.com/). Inertia.js is a framework that allows you to create fully client-side rendered, single-page apps, without much of the complexity that comes with modern SPAs. It does this by leveraging Leaf's server-side rendering capabilities.
+
+[> Read the docs](/modules/views/inertia/)
+
+## Next Steps
+
+You can continue learning about MVC with Leaf from the sidebar or check out the view engines below:
+
+
diff --git a/apps/docs/src/docs/routing/controller.md b/apps/docs/src/docs/routing/controller.md
new file mode 100755
index 0000000..8c9bc71
--- /dev/null
+++ b/apps/docs/src/docs/routing/controller.md
@@ -0,0 +1,172 @@
+# Using Controllers
+
+
+Controllers are simply classes that serve as bridges between Models and the View part of your application. Don't think too much of controllers, they're nothing but a class.
+
+In this section, we'll be looking at how to handle a route with a controller. So let's make an example controller: **remember it's just a php class**
+
+```php
+
+
+```php
+require "HomeController.php";
+
+// we leave out the second parameter for now
+app()->get("/home");
+```
+
+
+
+
+```php
+$app = new Leaf\App;
+
+require "HomeController.php";
+
+// we leave out the second parameter for now
+$app->get("/home");
+```
+
+
+
+When using controllers, instead of defining a closure or function as the second parameter of your route, you rather pass in a string of the controller's class name and the function you want to use. In this case, `"HomeController@index"`, so remember, it's `Class@Method`
+
+
+
+## Controller Namespaces
+
+In case you're using an auto loader or using leaf in another framework and you have your controllers in another directory, you can do sommething like this
+
+
+
+But this gets tedious if you have a lot of routes. So Leaf allows you to set a "general" namespace, you can set the default namespace to use on your router instance via `setNamespace()`
+
+
+
+## Resource Controller
+
+Resource Controllers contain methods to handle CRUD functionality.
+
+```php
+request = new Request;
+ }
+
+ /**
+ * Display a listing of the resource.
+ */
+ public function index() {
+ //
+ }
+
+ /**
+ * Show the form for creating a new resource.
+ */
+ public function create() {
+ //
+ }
+
+ /**
+ * Store a newly created resource in storage.
+ */
+ public function store() {
+ //
+ }
+
+ /**
+ * Display the specified resource.
+ */
+ public function show($id) {
+ //
+ }
+
+ /**
+ * Show the form for editing the specified resource.
+ */
+ public function edit($id) {
+ //
+ }
+
+ /**
+ * Update the specified resource in storage.
+ */
+ public function update($id) {
+ //
+ }
+
+ /**
+ * Remove the specified resource from storage.
+ */
+ public function destroy($id) {
+ //
+ }
+}
+```
diff --git a/apps/docs/src/docs/routing/dynamic.md b/apps/docs/src/docs/routing/dynamic.md
new file mode 100755
index 0000000..0b76dc9
--- /dev/null
+++ b/apps/docs/src/docs/routing/dynamic.md
@@ -0,0 +1,147 @@
+# Dynamic Routing
+
+
+
+
+
+
+*This guide assumes you have read [Simple Routing](/docs/routing/)*
+
+Dynamic routing is the idea of creating routes that can be accessed dynamically. For example, you can create a route that accepts a user id and displays the user with that id. This is useful when you want to create routes that can be accessed using dynamic information like ids, usernames, etc.
+
+Leaf router provides two ways to create dynamic routes:
+
+- [Dynamic Placeholder-based Route Patterns](#named-params)
+- [PCRE-based Route Patterns](#pcre-based-params)
+
+## Named Params
+
+Dynamic Placeholder-based Route Patterns are the same as Dynamic PCRE-based Route Patterns, but with one difference: they use human readable placeholders instead of regular expressions. Placeholders are strings surrounded by curly braces, e.g. {name}. You don't need to add parens around placeholders.
+
+Examples
+
+- `/movies/{id}`
+- `/profile/{username}`
+
+These placeholders are easier to use than PRCEs, but offer you less control as they internally get translated to a PRCE that matches any character (.*).
+
+
+
+**Note:** the name of the placeholder does not need to match with the name of the parameter that is passed into the route handling function...although it's adviced:
+
+
+
+## PCRE Based Params
+
+Basically, PCRE based patterns are just another way to use routes dynamically. This type of Route Patterns contain dynamic parts which can vary per request. The varying parts are named subpatterns and are defined using regular expressions.
+
+Examples
+
+- /movies/(\d+)
+- /profile/(\w+)
+
+Commonly used PCRE-based subpatterns within Dynamic Route Patterns are:
+
+- \d+ = One or more digits (0-9)
+- \w+ = One or more word characters (a-z 0-9 _)
+- [a-z0-9_-]+ = One or more word characters (a-z 0-9 _) and the dash (-)
+- .* = Any character (including /), zero or more
+- [^/]+ = Any character but /, one or more
+
+Note: The PHP PCRE Cheat Sheet might come in handy.
+
+The subpatterns defined in Dynamic PCRE-based Route Patterns are converted to parameters which are passed into the route handling function. Prerequisite is that these subpatterns need to be defined as parenthesized subpatterns, which means that they should be wrapped between parens:
+
+
+
+```php
+// Bad
+app()->get('/hello/\w+', function ($name) {
+ echo 'Hello ' . htmlentities($name);
+});
+
+// Good
+app()->get('/hello/(\w+)', function ($name) {
+ echo 'Hello ' . htmlentities($name);
+});
+```
+
+
+
+
+```php
+// Bad
+$app->get('/hello/\w+', function ($name) {
+ echo 'Hello ' . htmlentities($name);
+});
+
+// Good
+$app->get('/hello/(\w+)', function ($name) {
+ echo 'Hello ' . htmlentities($name);
+});
+```
+
+
+
+**Note**: The leading `/` at the very beginning of a route pattern is not mandatory, but is recommended.
+
+When multiple subpatterns are defined, the resulting route handling parameters are passed into the route handling function in the order they are defined in:
+
+
diff --git a/apps/docs/src/docs/routing/errors.md b/apps/docs/src/docs/routing/errors.md
new file mode 100755
index 0000000..606719d
--- /dev/null
+++ b/apps/docs/src/docs/routing/errors.md
@@ -0,0 +1,146 @@
+# Error Handling
+
+
+
+
+
+
+
+By default Leaf has error screens which are displayed for application exceptions, 404s and production server errors, however, Leaf also gives you full control and allows you to customize what is shown when an error or exception is encountered.
+
+## Handling 404
+
+Leaf displays a 404 screen for users, however, it may not always be appropriate, especially when you're building an API. You will probably want to return JSON instead of markup. For cases like this, Leaf has prepared a `set404` method on the Leaf instance.
+
+This method allows you to customize what a user sees when they visit a route that doesn't exist in your application. It takes in one parameter, a callable in the form of a function or an array.
+
+The example below displays a custom 404 page.
+
+
+
+## Handling 500
+
+Server errors are a bit more complicated because there are 2 states displayed to the user. The first is a general error state used in development. That screen gives you details on errors that occur during development. If you've ever run into an error during development, you've probably come across a nice looking page that gives you information about your error, line numbers and all that.
+
+The second screen is shown when debugging is turned off. This screen is intended to not give any details on the error, but rather log out issues in the background. To get a preview of this screen locally, you can configure Leaf's `debug` to `false`.
+
+
+
+```php
+$app = new \Leaf\App('debug', false);
+```
+
+
+
+You'll have an error page which doesn't give details on the error, however, if logs are enabled, all the errors are saved to a log file in the background.
+
+### Setting your own error screen
+
+Although Leaf handles both debug and production cases, you may want to display your own error/exception screens instead of going with the Leaf defaults. For cases like this, you should use the `setErrorHandler` method on the Leaf instance.
+
+This method takes in a callable in the form of a function or an array. You can take a look at the exaples below:
+
+
+
+```php
+// use an error handler from a package
+$app->setErrorHandler(['\Leaf\Exception\General', 'defaultError']);
+
+// use a custom function
+$app->setErrorHandler(function () use($app) {
+ $app->response()->page('./pages/500.html');
+});
+```
+
+
+
+
+```php
+// use an error handler from a package
+app()->setErrorHandler(['\Leaf\Exception\General', 'defaultError']);
+
+// use a custom function
+app()->setErrorHandler(function () {
+ response()->page('./pages/500.html');
+});
+```
+
+
+
+## Application Down
+
+Leaf is also able to dynamically handle placing your application in maintenance mode using leaf config. We have a `down` config which you can set to `true` to place your application in maintenance mode.
+
+
+
+Alternatively, you could also place your application in maintenance mode by setting the `APP_DOWN` environment variable to true. Since `.env` variables are given more priority than router config, the router config will be ignored as long as the env is set. If you decide to use the env variable, you will have to manually load your `.env` file. Check out the [env docs](/docs/config/nsm) for more info.
+
+::: tip Loading your env
+Your environment variables are automatically loaded into your application if you are using Leaf MVC, Leaf API or Skeleton.
+:::
+
+### Custom Down Handler
+
+Leaf comes with a beautiful application down handler which you can use in production. However, it might not match your theme, or you might have a maintenance screen designed by someone which needs to match that design. Leaf gives you the flexibility to display a custom maintenance error page using the `setDown` method.
+
+
diff --git a/apps/docs/src/docs/routing/index.md b/apps/docs/src/docs/routing/index.md
new file mode 100755
index 0000000..51aaeba
--- /dev/null
+++ b/apps/docs/src/docs/routing/index.md
@@ -0,0 +1,733 @@
+
+# Basic Routing
+
+
+
+::: tip
+Leaf router is now separated from Leaf and is now available as an installable module via composer or the leaf cli.
+
+
+Using Leaf router outside leaf
+
+To use Leaf router outside of a leaf app, simply install the leaf router module:
+
+```bash
+composer require leafs/router
+```
+
+or
+
+```bash
+leaf install router
+```
+
+After this, you can use all of leaf router's functionality with the router class below.
+
+## Router class
+
+The router class is the interface you interact with to perform any routing actions in your app. Leaf core directly integrates with the router class, which means that there is no need to use this class directly, if however, you are using leaf router outside of leaf, you will need to use the router class itself.
+
+```php
+use Leaf\Router;
+
+Router::get('/', 'PagesController@index');
+
+Router::run();
+```
+
+
+:::
+
+Leaf router uses a single root file, to which all the server requests are redirected, it then takes these requests and matches them to rules you have defined. The results are then displayed to the user. Then entire routing process is actually based on this simple concept.
+
+To make things simpler, we tied leaf router directly to the leaf instance, so once you initialize leaf, you can use routing.
+
+
+
+
+
+## Using a different router in Leaf
+
+Although Leaf integrates leaf router directly, you are free to import and use any router you want.
+
+1. Install whatever you want
+
+```bash
+composer require imaginary/router
+```
+
+2. Import and use it in your project
+
+
+
+```php
+// initialise imaginary router
+$imr = new Imaginary\Router();
+
+$imr->get('/', function () {
+ // you can still use leaf modules
+ response()->json(['title' => 'hello']);
+});
+```
+
+
+
+
+```php
+// initialise imaginary router
+$imr = new Imaginary\Router();
+$response = new Leaf\Http\Response();
+
+$imr->get('/', function () use($response) {
+ // you can still use leaf modules
+ $response->json(['title' => 'hello']);
+});
+```
+
+
+
+## Creating Routes
+
+::: warning IMPORTANT
+From this point onwards, we will assume that you are using Leaf router inside a leaf app, as such, we will use the app syntax:
+
+
+
+```php
+app()->get('/', function () {...});
+```
+
+
+
+
+```php
+$app->get('/', function () {...});
+```
+
+
+
+If however, you are using leaf router outside leaf, simply change `app()`/`$app` to the router class:
+
+```php
+Router::get('/', function () {...});
+```
+
+:::
+
+You can define application routes using proxy methods on the Leaf\App instance. Leaf supports different types of requests, let's look at them.
+
+### GET
+
+You can add a route that handles only GET HTTP requests with the Leaf router's get() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+
+
+```php
+app()->get('/home', function () {
+ // your code
+});
+```
+
+
+
+
+```php
+$app->get('/home', function () {
+ // your code
+});
+```
+
+
+
+### POST
+
+You can add a route that handles only POST HTTP requests with the Leaf router's post() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+
+
+```php
+app()->post('/users/add', function () {
+ $user = request()->get('user');
+ // create a new user
+});
+```
+
+
+
+
+```php
+$app->post('/users/add', function () use($request) {
+ $user = $request->get('user');
+ // create a new user
+});
+```
+
+
+
+Using Post Params
+View [Request](/modules/http/v/2/request) for more info on handling params
+
+### PUT requests
+
+You can add a route that handles only PUT HTTP requests with the Leaf router’s put() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+
+
+```php
+app()->put('/book/edit/{id}', function ($id) {
+ // your code
+});
+```
+
+
+
+
+```php
+$app->put('/book/edit/{id}', function ($id) {
+ // your code
+});
+```
+
+
+
+### DELETE requests
+
+You can add a route that handles only DELETE HTTP requests with the Leaf router's delete() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+
+
+### OPTIONS requests
+
+You can add a route that handles only OPTIONS HTTP requests with the Leaf router's options() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+
+
+### PATCH requests
+
+You can add a route that handles only PATCH HTTP requests with the Leaf router's patch() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+
+
+```php
+app()->patch('/post/{id}', function ($id) {
+ // your code
+});
+```
+
+
+
+
+```php
+$app->patch('/post/{id}', function ($id) {
+ // your code
+});
+```
+
+
+
+### ALL requests
+
+You can add a route that handles all HTTP requests with the Leaf router's all() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+
+
+```php
+app()->all('/post/{id}', function ($id) {
+ // your code
+});
+```
+
+
+
+
+```php
+$app->all('/post/{id}', function ($id) {
+ // your code
+});
+```
+
+
+
+### Resource Routes
+
+This section assumes you've read [working with controllers](/docs/routing/controller). In an MVC application, controllers play a major role as they're the bridge between your view and your model.
+
+A resource route simply creates all the routes needed to successfully handle a particular feature. This sounds a bit bleak, let's look at an example.
+
+
+
+Resource routes are handled by a [resource controller](/docs/routing/controller?id=resource-controller).
+
+## Route "Hooking"
+
+You can add a route that handles a couple of HTTP methods with the Leaf router's match() method. It accepts three arguments:
+
+- The HTTP method(s) seperated by `|`
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+
+
+```php
+app()->match('GET|POST', '/people', function () {
+ // your code
+});
+```
+
+
+
+
+```php
+$app->match('GET|POST', '/people', function () {
+ // your code
+});
+```
+
+
+
+## Running your routes
+
+After setting all the routes, you'll need to dispatch the routes. This is achieved through Leaf's run() method.
+
+
+
+```php
+app()->run();
+```
+
+
+
+
+```php
+$app->run();
+```
+
+
+
+## Route options
+
+Route options simply allow you to configure the way groups and individual routes by passing in additional parameters. In actual sense, all new features were generated as a result of this single feature. Let's see how it works.
+
+Leaf route handlers are usually callable functions like this:
+
+
+
+When an array is passed into a leaf route as the handler, leaf will take all `key => value` as options for that route, the first non key-value `function` or `controller` in the array is taken as the handler.
+
+
+
+**This doesn't mean that you should always pass in an array, if you don't need the other options, you can pass in your function or controller directly as you've always done.**
+
+## Naming your routes
+
+You can give names to your routes which allows you to use your route names for navigation instead of your route paths. This feature is heavily inspired by vue-router.
+
+
+
+```php
+app()->get('/movies/{movieId}/photos/{photoId}', ['name' => 'moviesAndPhotos', function () {
+ echo 'User Movies and Photos';
+}]);
+
+app()->route('moviesAndPhotos', ['movieId' => 'my-movie', 'photoId' => 'my-photo']);
+
+// Would return: /movies/my-movie/photos/my-photo
+```
+
+
+
+
+```php
+$app->get('/movies/{movieId}/photos/{photoId}', ['name' => 'moviesAndPhotos', function () {
+ echo 'User Movies and Photos';
+}]);
+
+$app->route('moviesAndPhotos', ['movieId' => 'my-movie', 'photoId' => 'my-photo']);
+
+// Would return: /movies/my-movie/photos/my-photo
+```
+
+
+
+### Pushing to a route
+
+This is simply redirecting to a route and can be done using `push`. `push` also allows you to reference the route by it's name instead of it's path.
+
+
+
+```php
+app()->push('/home');
+```
+
+
+
+
+```php
+$app->push('/home');
+```
+
+
+
+When an array is passed into push, Leaf will search for a route name matching the string in the array and redirect to that route:
+
+
+
+```php
+// home was defined above
+app()->push(['home']);
+```
+
+
+
+
+```php
+// home was defined above
+$app->push(['home']);
+```
+
+
+
+## Getting the current route
+
+There are times when you need to get information about the current route from within one of your route handlers, views or controllers. For example, you may wish to generate a URL for the current route or redirect to the current route. You may also need to access the route's parameters. You may do all of this using the `Router::getRoute()` method:
+
+
+
+The output of `getRoute()` looks something like this:
+
+```json
+{
+ "pattern": "/users/(.*?)",
+ "path": "/users/1",
+ "method": "GET",
+ "name": "mycontroller",
+ "handler": "MyNamespace\\Controller@index",
+ "params": [
+ "1"
+ ]
+}
+```
+
+- `pattern` is the route pattern
+- `path` is the current route path
+- `method` is the current route method
+- `name` is the current route name
+- `handler` is the current route handler (custom function or controller)
+- `params` is an array of the current route's parameters (dynamic values)
diff --git a/apps/docs/src/docs/routing/middleware.md b/apps/docs/src/docs/routing/middleware.md
new file mode 100755
index 0000000..463c6d8
--- /dev/null
+++ b/apps/docs/src/docs/routing/middleware.md
@@ -0,0 +1,492 @@
+
+# Middleware
+
+
+
+
+
+## What is middleware?
+
+In simple terms, middleware is a piece of code that runs before your application runs. It can be used to perform various tasks like authentication, error handling, logging, etc. It can also help to optimize the performance of an application by caching data, compressing responses, or distributing load across multiple servers, although those are more advanced use-cases. It's a great way to keep your code clean and organized.
+
+::: tip Note
+Leaf has modules that can be used to perform some of the tasks middleware can do. For example, Leaf has [Leaf Auth](/modules/auth/v/2.1/) which can be used to handle authentication, a [logger module](/docs/tooling/logging) and many more useful [modules](/modules/). This means you don't need to use middleware to perform these tasks. However, middleware can be used to perform tasks that Leaf modules don't cover.
+:::
+
+## How does middleware work?
+
+Middleware is a concept that is used in many frameworks, and they have different ways of implementing it. Leaf's implementation is based on the concept of middleware stacks. A middleware stack is a list of middleware that are executed in a specific order. The order is important because each middleware can perform a task and pass the request to the next middleware in the stack. This is how middleware works in Leaf.
+
+When a request is made to your application, Leaf will run through the middleware stack and execute each middleware. After the middleware stack is done, Leaf will then execute the route handling function. This is a rough overview of how middleware works in Leaf, however, for a more in-depth explanation, you can check out the video by [Codecourse](https://www.codecourse.com).
+
+
+
+## Middleware in Leaf
+
+Leaf provides 2 interfaces for middleware: application middleware and router hooks.
+
+- Router hooks basically hook into the runtime of the Leaf router and allow you to run code before a route/multiple routes are invoked.
+- Application middleware on the other hand is a more structured way to define and use middleware in your apps. It allows you to define middleware classes as done in other frameworks like Laravel. This fits right in if you intend to build MVC applications.
+
+## Application middleware
+
+As mentioned above, application middleware gives you a more structured way to define and use middleware in your apps. It allows you to define middleware as classes instead of using functions.
+
+### Defining application middleware
+
+Leaf provides a `Middleware` class that you can extend to define your application middleware. The `Middleware` class has a `call` method that you can override to define your middleware logic. In the `call` method, you can perform any task you want and then call `$this->next()` to pass the request to the next middleware in the stack or even return a response if you want to break the execution of your application. The example below checks if a request key is set, if it's not, the user is redirected to another route.
+
+```php
+class TestMiddleware extends Leaf\Middleware
+{
+ public function call()
+ {
+ if (!request()->get('key')) {
+ return Custom::redirect('/login');
+ }
+
+ return $this->next();
+ }
+}
+```
+
+One thing to note is you should always call `$this->next()`. The `$this->next()` method forwards the incoming request to the next middleware or your application if there's no other middleware.
+
+### Using your application middleware
+
+After defining the middleware, the next step is to tell Leaf to actually run your middleware. You can do this by calling the `use` method on the Leaf instance.
+
+
+
+```php
+$app = new Leaf\App();
+
+$app->use(new TestMiddleware);
+
+// ... your routes here
+```
+
+
+
+
+```php
+app()->use(new TestMiddleware);
+
+// ... your routes here
+```
+
+
+
+## Before Route Middlewares
+
+This is a type of router hook that runs before a particular route is invoked. It is technically just a callable/function that holds whatever code you want to execute before the route is executed. To actually create and register the before route middleware, you need to pass the function into the `before` method of the Leaf instance.
+
+The `before` method takes 3 arguments:
+
+- The HTTP method: This can be a single method or a pipe-separated list of methods.
+- The route pattern
+- The middleware function
+
+This example below shows how to create a before route middleware that checks if a user is logged in before allowing access to the admin dashboard. Note that we're using `/admin/.*` as the route pattern. This means that the middleware will be executed for all routes that start with `/admin/`.
+
+
+
+### Matching multiple middleware
+
+Unlike route handling functions, more than one before route middleware is executed when more than one route match is found.
+
+
+
+```php
+$app->before('GET|POST', '/admin/.*', function () {
+ if (!isset($_SESSION['user'])) {
+ header('location: /auth/login');
+ exit();
+ }
+});
+
+$app->before('GET|POST', '/admin/.*', function () {
+ if (!isset($_SESSION['user_secret'])) {
+ header('location: /auth/login');
+ exit();
+ }
+});
+```
+
+
+
+
+```php
+app()->before('GET|POST', '/admin/.*', function () {
+ if (!isset($_SESSION['user'])) {
+ header('location: /auth/login');
+ exit();
+ }
+});
+
+app()->before('GET|POST', '/admin/.*', function () {
+ if (!isset($_SESSION['user_secret'])) {
+ header('location: /auth/login');
+ exit();
+ }
+});
+```
+
+
+
+Using this same concept, you can run your middleware on every route. We call this before router middleware.
+
+## Before Router Middlewares
+
+Before route middlewares are route specific. Using a general route pattern (viz. all URLs), they can become Before Router Middlewares (in other projects sometimes referred to as before app middlewares) which are always executed, no matter what the requested URL is.
+
+
+
+```php
+$app->before('GET', '/.*', function () {
+ // ... this will always be executed
+});
+```
+
+
+
+
+```php
+app()->before('GET', '/.*', function () {
+ // ... this will always be executed
+});
+```
+
+
+
+As you can see, the only difference between before route and before router middleware is the route pattern.
+
+## Middleware route option
+
+This is a new way to quickly setup middleware for a particular route. Leaf has the before method which allows you to set a route specific middleware, but that means defining the same route twice, not to mention, you may mistake the middleware for the main route as they have the same syntax. This problem is solved by the middleware option. **If your prefer using `before`, you can always do so.**
+
+Let's take this function which we're using as our middleware:
+
+```php
+$midfn = function () {
+ echo 'Home middleware';
+};
+```
+
+We can use this middleware directly on our route like this:
+
+
+
+## Named Middleware Route Options New
+
+You can name your middleware and use it on multiple routes. This is useful when you have a lot of routes that use the same middleware. You can name your middleware like this:
+
+
+
+Named middleware can also be used with route groups:
+
+
+
+```php
+$app->registerMiddleware('home', function () {
+ echo 'Home middleware';
+});
+
+$app->group('/group', ['middleware' => 'home', function () use ($app) {
+ $app->get('/home', function () { ... });
+ $app->get('/home/about', function () { ... });
+}]);
+```
+
+
+
+
+
+```php
+app()->registerMiddleware('home', function () {
+ echo 'Home middleware';
+});
+
+app()->group('/group', ['middleware' => 'home', function () {
+ app()->get('/home', function () { ... });
+ app()->get('/home/about', function () { ... });
+}]);
+```
+
+
+
+## Router Hooks
+
+Hooks basically allow you to hook into Leaf router and execute a callback at a given time. For instance, you can execute a function just before Leaf fires off routes. You can also execute a callback before the main middleware executes or even after Leaf has completely executed a route.
+
+There are 6 hooks that you can now use with Leaf router listed below in execution order:
+
+**It doesn't matter the order in which you define hooks. Leaf router will run them in the correct order.**
+
+### `router.before`
+
+This hook runs before Leaf router begins any operations, even before app middleware are triggered.
+
+
+
+```php
+$app->hook('router.before', function () {
+ // do something
+});
+```
+
+
+
+
+```php
+app()->hook('router.before', function () {
+ // do something
+});
+```
+
+
+
+### `router.before.route`
+
+This hook runs just after the app middleware have run, just before the route specific middleware.
+
+
+
+```php
+$app->hook('router.before.route', function () {
+ // do something
+});
+```
+
+
+
+
+```php
+app()->hook('router.before.route', function () {
+ // do something
+});
+```
+
+
+
+### `router.before.dispatch`
+
+This hook runs just before routes are dispatched.
+
+
+
+```php
+$app->hook('router.before.dispatch', function () {
+ // do something
+});
+```
+
+
+
+
+```php
+app()->hook('router.before.dispatch', function () {
+ // do something
+});
+```
+
+
+
+### `router.after.dispatch`
+
+This hook runs just after routes are dispatched.
+
+
+
+```php
+$app->hook('router.after.dispatch', function () {
+ // do something
+});
+```
+
+
+
+
+```php
+app()->hook('router.after.dispatch', function () {
+ // do something
+});
+```
+
+
+
+### `router.after.route`
+
+This hook runs after Leaf router has finished up with routing and cleaning up, just before the execution of internal code.
+
+
+
+```php
+$app->hook('router.after.route', function () {
+ // do something
+});
+```
+
+
+
+
+```php
+app()->hook('router.after.route', function () {
+ // do something
+});
+```
+
+
+
+### `router.after`
+
+This hook runs when leaf completely finishes route execution and cleans up on the internal code as well. This is the last thing Leaf router does before exiting.
+
+
+
+```php
+$app->hook('router.after', function () {
+ // do something
+});
+```
+
+
+
+
+```php
+app()->hook('router.after', function () {
+ // do something
+});
+```
+
+
+
+::: tip Note
+Unlike the above hooks, `router.after` can be directly assigned by passing a function into Leaf router's `run` method.
+
+
+
+```php
+$app = new Leaf\App;
+
+// define routes
+
+$app->run(function () {
+ echo "Final thing to run";
+});
+```
+
+
+
+
+```php
+// define routes
+
+app()->run(function () {
+ echo "Final thing to run";
+});
+```
+
+
+
+Also note that the final function may return a value for further use if need be.
+
+
+
+:::
diff --git a/apps/docs/src/docs/routing/mvc.md b/apps/docs/src/docs/routing/mvc.md
new file mode 100644
index 0000000..d90e327
--- /dev/null
+++ b/apps/docs/src/docs/routing/mvc.md
@@ -0,0 +1,103 @@
+# Usage with Leaf MVC
+
+Leaf MVC and Leaf API use a routing system based on the [Leaf Core](/docs/routing/) router. This means you can use all of Leaf Core's routing methods on your Leaf MVC and Leaf API apps. You can find the full docs [here](/docs/routing/).
+
+## Adding Routes
+
+Leaf MVC and Leaf API come with an `app/routes` folder. This folder contains all your route files. The `app/routes/index.php` file is the entry point for all your routes and is where you can add all your routes.
+
+## Route Partials
+
+New versions of Leaf MVC and Leaf API come with a new feature called route partials. Route partials allow you to split your routes into multiple files. This is useful if you have a lot of routes and you want to split them into multiple files. To use route partials, you can create a new file in your `app/routes` folder and add your routes to it.
+
+All files in your `app/routes` folder that start with `_` are automatically loaded by Leaf MVC and Leaf API. This means that you can create a file called `app/routes/_auth.php` and add all your auth routes to it. This file will be automatically loaded by Leaf MVC and Leaf API, and all the routes in it will be available in your app.
+
+If you don't want a file to be automatically loaded, create a file that doesn't start with `_`. For example, you can create a file called `app/routes/auth.php` and add all your auth routes to it. This file will **NOT** be automatically loaded by Leaf MVC and Leaf API, and you'll have to load it manually.
+
+## Linking Controllers
+
+Controllers provide a way to organize your application's logic. They are a great way to keep your routes file clean and easy to read. Controllers are just classes that implement the `App\Controllers\Controller` class. You can find more information about controllers in the [Controllers](/docs/mvc/controllers) document. This document will focus on how to link controllers to routes.
+
+Leaf provides a simple interface for interacting with controllers and their methods from inside your routes. The idea is to run a method inside your controller whenever a route is matched. This is done by telling Leaf which controller to use and which method to run. Leaf will then create an instance of the controller and run the method.
+
+### Example
+
+Let's say you have a controller called `App\Controllers\HomeController` and you want to run the `index` method inside it whenever the `/` route is matched. You can do this by passing a string containing your controller name and method to your route. The string should be in the format `controllerName@methodName`.
+
+
+
+## Controller Namespaces
+
+***This has already been setup for you in the root routes file of your MVC application.***
+
+In case you're using an auto loader or using leaf in another framework and you have your controllers in another directory, you can do sommething like this
+
+
+
+But this gets tedious if you have a lot of routes. So Leaf allows you to set a "general" namespace, you can set the default namespace to use on your router instance via `setNamespace()`
+
+
diff --git a/apps/docs/src/docs/routing/sub-folder.md b/apps/docs/src/docs/routing/sub-folder.md
new file mode 100755
index 0000000..5ef3c93
--- /dev/null
+++ b/apps/docs/src/docs/routing/sub-folder.md
@@ -0,0 +1,62 @@
+# Sub-folder support
+
+
+## Overview
+
+Out-of-the box Leaf's Core router will run in any (sub)folder you place it into … no adjustments to your code are needed. You can freely move your entry script index.php around, and the router will automatically adapt itself to work relatively from the current folder's path by mounting all routes onto that basePath.
+
+Say you have a server hosting the domain www.example.org using public_html/ as its document root, with this little entry script index.php:
+
+
+
+```php
+app()->get('/', function () { echo 'Index'; });
+app()->get('/hello', function () { echo 'Hello!'; });
+```
+
+
+
+
+```php
+$app->get('/', function () { echo 'Index'; });
+$app->get('/hello', function () { echo 'Hello!'; });
+```
+
+
+
+- If your were to place this file (along with its accompanying .htaccess file or the like) at the document root level (e.g. public_html/index.php), Leaf's Core router will mount all routes onto the domain root (e.g. /) and thus respond to [https://www.example.org/](https://www.example.org/) and [https://www.example.org/hello](https://www.example.org/hello).
+
+- If you were to move this file (along with its accompanying .htaccess file or the like) into a subfolder (e.g. public_html/demo/index.php), Leaf's Core router will mount all routes onto the current path (e.g. /demo) and thus repsond to [https://www.example.org/demo](https://www.example.org/demo) and [https://www.example.org/demo/hello](https://www.example.org/demo/hello). There's no need for `$app->mount(…)` in this case.
+
+## Disabling subfolder support
+
+In case you don't want Leaf's Core router to automatically adapt itself to the folder its being placed in, it's possible to manually override the basePath by calling `setBasePath()`. This is necessary in the (uncommon) situation where your entry script and your entry URLs are not tightly coupled (e.g. when the entry script is placed into a subfolder that does not need be part of the URLs it responds to)..
+
+
+
+```php
+// Override auto base path detection
+app()->setBasePath('/');
+
+app()->get('/', function () { echo 'Index'; });
+app()->get('/hello', function () { echo 'Hello!'; });
+
+app()->run();
+```
+
+
+
+
+```php
+// Override auto base path detection
+$app->setBasePath('/');
+
+$app->get('/', function () { echo 'Index'; });
+$app->get('/hello', function () { echo 'Hello!'; });
+
+$app->run();
+```
+
+
+
+If you were to place this file into a subfolder (e.g. public_html/some/sub/folder/index.php), it will still mount the routes onto the domain root (e.g. /) and thus respond to [https://www.example.org/](https://www.example.org/) and [https://www.example.org/hello](https://www.example.org/hello) (given that your .htaccess file – placed at the document root level – rewrites requests to it)
diff --git a/apps/docs/src/docs/routing/sub-patterns.md b/apps/docs/src/docs/routing/sub-patterns.md
new file mode 100755
index 0000000..8a577a0
--- /dev/null
+++ b/apps/docs/src/docs/routing/sub-patterns.md
@@ -0,0 +1,82 @@
+# Optional Route Subpatterns
+
+
+
+*This guide assumes you have read [Simple Routing](/docs/routing/) and [dynamic routing](/docs/routing/dynamic)*
+
+Route subpatterns can be made optional by making the subpatterns optional by adding a ? after them. Think of blog URLs in the form of /blog(/year)(/month)(/day)(/slug):
+
+
+
+```php
+app()->get('/blog(/\d+)?(/\d+)?(/\d+)?(/[a-z0-9_-]+)?', function ($year = null, $month = null, $day = null, $slug = null) {
+ if (!$year) { echo 'Blog overview'; return; }
+ if (!$month) { echo 'Blog year overview'; return; }
+ if (!$day) { echo 'Blog month overview'; return; }
+ if (!$slug) { echo 'Blog day overview'; return; }
+ echo 'Blogpost ' . htmlentities($slug) . ' detail';
+});
+```
+
+
+
+
+```php
+$app->get('/blog(/\d+)?(/\d+)?(/\d+)?(/[a-z0-9_-]+)?', function ($year = null, $month = null, $day = null, $slug = null) {
+ if (!$year) { echo 'Blog overview'; return; }
+ if (!$month) { echo 'Blog year overview'; return; }
+ if (!$day) { echo 'Blog month overview'; return; }
+ if (!$slug) { echo 'Blog day overview'; return; }
+ echo 'Blogpost ' . htmlentities($slug) . ' detail';
+});
+```
+
+
+
+The code snippet above responds to the URLs /blog, /blog/year, /blog/year/month, /blog/year/month/day, and /blog/year/month/day/slug.
+
+**Note**: With optional parameters it is important that the leading / of the subpatterns is put inside the subpattern itself. Don't forget to set default values for the optional parameters.
+
+The code snipped above unfortunately also responds to URLs like /blog/foo and states that the overview needs to be shown - which is incorrect. Optional subpatterns can be made successive by extending the parenthesized subpatterns so that they contain the other optional subpatterns: The pattern should resemble /blog(/year(/month(/day(/slug)))) instead of the previous /blog(/year)(/month)(/day)(/slug):
+
+
+
+**Note**: It is highly recommended to always define successive optional parameters.
+
+To make things complete use [quantifiers](http://www.php.net/manual/en/regexp.reference.repetition.php) to require the correct amount of numbers in the URL:
+
+
diff --git a/apps/docs/src/docs/routing/sub-routing.md b/apps/docs/src/docs/routing/sub-routing.md
new file mode 100755
index 0000000..7a7a1a3
--- /dev/null
+++ b/apps/docs/src/docs/routing/sub-routing.md
@@ -0,0 +1,240 @@
+# Sub-routing
+
+
+Sub-routing is the idea of grouping your routes into smaller groups. This is useful when you want to group your routes into different sections. For example, you can group all your admin routes into a group called `admin` and all your user routes into a group called `user`.
+
+There are many benefits to this, one of which is that you can easily prefix your routes with a path. For example, you can prefix all your admin routes with `/admin` and all your user routes with `/user`. You can also add shared middleware to a group of routes.
+
+## Creating a route group
+
+Leaf router provides a `group()` method which allows you to create a group of routes. The `group` method accepts two parameters, the first is the path to the group and the second is a callback function which contains all the routes in the group.
+
+The path to the group is the prefix that will be added to all the routes in the group. For example, if you create a group with the path `/admin`, all the routes in the group will be prefixed with `/admin`. **If you want to opt out of the prefix for a route group, you can set the group path to `/`.**
+
+
+
+```php
+app()->group('/movies', function () {
+ // will result in '/movies/'
+ app()->get('/', function () {
+ echo 'movies overview';
+ });
+
+ // will result in '/movies/id'
+ app()->get('/(\d+)', function ($id) {
+ echo 'movie id ' . htmlentities($id);
+ });
+});
+```
+
+
+
+
+```php
+$app->group('/movies', function () use($app) {
+ // will result in '/movies/'
+ $app->get('/', function () {
+ echo 'movies overview';
+ });
+
+ // will result in '/movies/id'
+ $app->get('/(\d+)', function ($id) {
+ echo 'movie id ' . htmlentities($id);
+ });
+});
+```
+
+
+
+## Nesting Groups
+
+Nesting of subroutes is possible, just define a second `group()` in the callback function that's already contained within a preceding `group()`.
+
+
+
+```php{2,12}
+// parent group
+app()->group('/user', function () {
+ app()->get('/', function () {
+ response()->markup('no user id');
+ });
+
+ app()->get('/(\d+)', function ($id) {
+ response()->markup("user $id");
+ });
+
+ // sub group here
+ app()->group('/settings', function () {
+ // will result in '/user/settings/privacy'
+ app()->get('/privacy', function () {
+ response()->markup('Privacy Settings');
+ });
+
+ app()->get('/notifications', function () {
+ response()->markup("Notification Settings");
+ });
+ });
+});
+```
+
+
+
+
+```php{2,12}
+// parent group
+$app->group('/user', function () use($app) {
+ $app->get('/', function () use($app) {
+ $app->response()->markup('no user id');
+ });
+
+ $app->get('/(\d+)', function ($id) use($app) {
+ $app->response()->markup("user $id");
+ });
+
+ // sub group here
+ $app->mount('/settings', function () use($app) {
+ // will result in '/user/settings/privacy'
+ $app->get('/privacy', function () use($app) {
+ $app->response()->markup('Privacy Settings');
+ });
+
+ $app->get('/notifications', function () use($app) {
+ $app->response()->markup('Notification Settings');
+ });
+ });
+});
+```
+
+
+
+## Group Namespaces
+
+When using controllers, Leaf allows you to set a global namespace for all your controllers. The problem with this is that you can't have different namespaces for different groups of routes, and that's where group namespaces come in.
+
+Leaf allows you to set a namespace for a group of routes. This means that all controllers in that group will be prefixed with the namespace you set for that group **instead of the global namespace**. This is useful when you want to have different namespaces for different groups of routes.
+
+
+
+```php
+app()->setNamespace('App\Controllers');
+
+
+app()->group('/user', ['namespace' => 'App\Controllers\Users', function () {
+ // controller here will be App\Controllers\Users\FormsController
+ app()->get('/form', 'FormsController@index');
+}]);
+
+
+app()->group('/admin', ['namespace' => 'App\Controllers\Admins', function () {
+ // controller here will be App\Controllers\Admins\FormsController
+ app()->get('/form', 'FormsController@index');
+}]);
+
+
+// controller here will be App\Controllers\FormsController
+app()->get('/form', 'FormsController@index');
+```
+
+
+
+
+```php
+$app->setNamespace('App\Controllers');
+
+
+$app->group('/user', ['namespace' => 'App\Controllers\Users', function () use($app) {
+ // controller here will be App\Controllers\Users\FormsController
+ $app->get('/form', 'FormsController@index');
+}]);
+
+
+$app->group('/admin', ['namespace' => 'App\Controllers\Admins', function () use($app) {
+ // controller here will be App\Controllers\Admins\FormsController
+ $app->get('/form', 'FormsController@index');
+}]);
+
+
+// controller here will be App\Controllers\FormsController
+$app->get('/form', 'FormsController@index');
+```
+
+
+
+For more info on controllers, you can [check out the controller docs](/docs/routing/controller).
+
+## Group Middleware
+
+You can also add middleware to a group of routes. This means that all routes in that group will be passed through the middleware you set for that group. This is useful when you want to add middleware to a group of routes.
+
+
+
+```php
+$middleware = function () {
+ // some middleware operation here
+};
+
+app()->group('/user', ['middleware' => $middleware, function () {
+ app()->get('/', function () {
+ response()->markup('no user id');
+ });
+
+ app()->get('/(\d+)', function ($id) {
+ response()->markup("user $id");
+ });
+}]);
+```
+
+
+
+
+```php
+$middleware = function () {
+ // some middleware operation here
+};
+
+$app->group('/user', ['middleware' => $middleware, function () use($app) {
+ $app->get('/', function () use($app) {
+ $app->response()->markup('no user id');
+ });
+
+ $app->get('/(\d+)', function ($id) use($app) {
+ $app->response()->markup("user $id");
+ });
+}]);
+```
+
+
+
+You can also use named middleware in a group of routes. This means that all routes in that group will be passed through the named middleware you set for that group. This is useful when you want to add named middleware to a group of routes.
+
+
+
+```php
+app()->group('/user', ['middleware' => 'auth', function () {
+ app()->get('/', function () {
+ response()->markup('no user id');
+ });
+
+ app()->get('/(\d+)', function ($id) {
+ response()->markup("user $id");
+ });
+}]);
+```
+
+
+
+
+
+```php
+$app->group('/user', ['middleware' => 'auth', function () use($app) {
+ $app->get('/', function () use($app) {
+ $app->response()->markup('no user id');
+ });
+
+ $app->get('/(\d+)', function ($id) use($app) {
+ $app->response()->markup("user $id");
+ });
+}]);
+```
+
+
diff --git a/apps/docs/src/docs/skeleton/index.md b/apps/docs/src/docs/skeleton/index.md
new file mode 100644
index 0000000..dca1222
--- /dev/null
+++ b/apps/docs/src/docs/skeleton/index.md
@@ -0,0 +1,93 @@
+
+
+# Skeleton
+
+Skeleton was designed to be a simple starting point for your application, and came with just the bare minimum to get you started. However, we have decided to deprecate Skeleton in favor of the Leaf CLI. The Leaf CLI is a command-line tool for generating Leaf projects, installing modules, and more.
+
+## Discontinuing Skeleton
+
+We recently released an update to the Leaf CLI that allows you to select specific features you want to include and generate a project with everything you need. This functionality is similar to what Skeleton provided but is more flexible and allows you to create projects with only the features that you need, which is why we've decided to deprecate Leaf Skeleton.
+
+Skeleton will still be available for download, but we won't be updating it anymore. We recommend that you generate a project with the CLI or use Leaf MVC or Leaf API instead.
+
+## Updating from Skeleton
+
+The beauty of this update is that you don't have to change anything in your existing Skeleton project. This is because Skeleton, unlike Leaf MVC and Leaf API is not a framework. It's just a starting point for your project. So, you can continue to use your Skeleton project and update specific features using the Leaf CLI or Composer.
+
+If you are starting a fresh project however, we recommend that you use the Leaf CLI to generate your project. If you want to use Composer, you can install Leaf MVC or Leaf API instead.
+
+**The Skeleton docs will still be available here, but we won't be updating it anymore.**
+
+## Directory Structure
+
+The Skeleton directory structure is setup to be straighforward and understandable at a glance. You can completely change the directory structure to suit your needs, just be sure to update the paths in the `config.php` file.
+
+For a fresh Skeleton app, the directory structure looks like this:
+
+```bash
+C:.
+├── config
+├── controllers
+├── models
+├── pages
+│ └── components
+├── routes
+├── storage
+│ ├── app
+│ │ └── public
+│ ├── framework
+│ │ └── views
+│ └── logs
+└── vendor
+```
+
+- ### The `config` directory
+
+ The `config` directory contains the configuration files for your application. These are used to configure how Leaf and it's modules interact with your application. You can find more information about the configuration files in the [Configuration](/docs/mvc/config) section.
+
+- ### The `controllers` directory
+
+ The `controllers` directory contains the controllers for your application. These are used to handle HTTP requests. You can find more information about the controllers in the [Controllers](/docs/mvc/controllers) section.
+
+- ### The `models` directory
+
+ The `models` directory contains the models for your application. These are used to interact with the database. You can find more information about the models in the [Models](/docs/mvc/models) section.
+
+- ### The `pages` directory
+
+ The `pages` directory contains the views and frontend assets for your Leaf application.
+
+- ### The `routes` directory
+
+ The `routes` directory contains routes for your application. These are used to map HTTP requests to controllers. You can find more information about the routes in the [Routing](/docs/mvc/routing) section.
+
+- ### The `storage` directory
+
+ The `storage` directory contains the compiled views, logs and other files generated by your application. It's divided into a few sub-directories:
+
+ - `app` - Contains the files generated by your application. This includes the compiled views and the files uploaded by users.
+ - `framework` - Contains the framework generated files for your application.
+ - `logs` - Contains the log files generated by your application.
+
+- ### The `vendor` directory
+
+ The `vendor` directory contains all the dependencies installed by Composer. It's automatically generated when you install the dependencies using Composer.
+
+## Next Steps
+
+Follow along with the next steps to learn more about Skeleton.
+
+
diff --git a/apps/docs/src/docs/tooling/app.md b/apps/docs/src/docs/tooling/app.md
new file mode 100644
index 0000000..f3c3011
--- /dev/null
+++ b/apps/docs/src/docs/tooling/app.md
@@ -0,0 +1,116 @@
+# Production Deployment
+
+::: tip
+Most of the tips below are enabled by default if you are using [Leaf CLI](/docs/cli/). This section is only relevant if you are using a custom build setup.
+:::
+
+## Turn on Production Mode
+
+During development, Leaf provides a lot of warnings to help you with common errors and pitfalls. However, these warning strings become useless in production and bloat your app's payload size. In addition, some of these warning checks have small runtime costs that can be avoided in production mode.
+
+### Without Build Tools
+
+If you are using the full build, i.e. directly including Leaf via a script tag without a build tool, make sure to use the minified version for production. This can be found in the [Installation guide](/docs/introduction/installation.html#cdn).
+
+### With Build Tools
+
+When using a build tool like Webpack or Browserify, the production mode will be determined by `process.env.NODE_ENV` inside Leaf's source code, and it will be in development mode by default. Both build tools provide ways to overwrite this variable to enable Leaf's production mode, and warnings will be stripped by minifiers during the build. Leaf CLI has this pre-configured for you, but it would be beneficial to know how it is done:
+
+#### Webpack
+
+In Webpack 4+, you can use the `mode` option:
+
+```js
+module.exports = {
+ mode: 'production'
+}
+```
+
+#### Browserify
+
+- Run your bundling command with the actual `NODE_ENV` environment variable set to `"production"`. This tells `Leafify` to avoid including hot-reload and development related code.
+
+- Apply a global [envify](https://github.com/hughsk/envify) transform to your bundle. This allows the minifier to strip out all the warnings in Leaf's source code wrapped in env variable conditional blocks. For example:
+
+ ```bash
+ NODE_ENV=production browserify -g envify -e main.js | uglifyjs -c -m > build.js
+ ```
+
+- Or, using [envify](https://github.com/hughsk/envify) with Gulp:
+
+ ```js
+ // Use the envify custom module to specify environment variables
+ const envify = require('envify/custom')
+
+ browserify(browserifyOptions)
+ .transform(Leafify)
+ .transform(
+ // Required in order to process node_modules files
+ { global: true },
+ envify({ NODE_ENV: 'production' })
+ )
+ .bundle()
+ ```
+
+- Or, using [envify](https://github.com/hughsk/envify) with Grunt and [grunt-browserify](https://github.com/jmreidy/grunt-browserify):
+
+ ```js
+ // Use the envify custom module to specify environment variables
+ const envify = require('envify/custom')
+
+ browserify: {
+ dist: {
+ options: {
+ // Function to deviate from grunt-browserify's default order
+ configure: (b) =>
+ b
+ .transform('Leafify')
+ .transform(
+ // Required in order to process node_modules files
+ { global: true },
+ envify({ NODE_ENV: 'production' })
+ )
+ .bundle()
+ }
+ }
+ }
+ ```
+
+#### Rollup
+
+Use [@rollup/plugin-replace](https://github.com/rollup/plugins/tree/master/packages/replace):
+
+```js
+const replace = require('@rollup/plugin-replace')
+
+rollup({
+ // ...
+ plugins: [
+ replace({
+ 'process.env.NODE_ENV': JSON.stringify( 'production' )
+ })
+ ]
+}).then(...)
+```
+
+## Pre-Compiling Templates
+
+When using in-DOM templates or in-JavaScript template strings, the template-to-render-function compilation is performed on the fly. This is usually fast enough in most cases, but is best avoided if your application is performance-sensitive.
+
+
+
+If you are using Webpack, and prefer separating JavaScript and template files, you can use [Leaf-template-loader](https://github.com/ktsn/Leaf-template-loader), which also transforms the template files into JavaScript render functions during the build step.
+
+## Extracting Component CSS
+
+When using Single-File Components, the CSS inside components are injected dynamically as `
diff --git a/apps/docs/src/ecosystem/themes/ThemeHero.vue b/apps/docs/src/ecosystem/themes/ThemeHero.vue
new file mode 100644
index 0000000..df9ec0e
--- /dev/null
+++ b/apps/docs/src/ecosystem/themes/ThemeHero.vue
@@ -0,0 +1,64 @@
+
+
+
+ Build anything with Leaf
+
+ Quickly get a project started with any of our examples ranging from using parts of the framework to modules and themes.
+
+
+
+
+
+
+ Want to feature your work here?
+ Contact us!
+
+
+
+This package is leaf's implementation of CSRF default protection with leaf anchor. It comes separated from leaf anchor because it is not needed in every project you may build.
+
+## Installation
+
+You can easily install Leaf CSRF using [Composer](https://getcomposer.org/).
+
+```bash
+composer require leafs/csrf
+```
+
+or with leaf CLI
+
+```bash
+leaf install csrf
+```
+
+## Basic Usage
+
+After installing leaf CSRF, leaf automatically loads the CSRF package for you, so you don't need to do anything unless you want to configure the CSRF module to match your application requirements.
+
+### Using CSRF outside of leaf
+
+Most leaf modules can be used outside of leaf. This module is one of these global modules. If you decide to use the CSRF module outside of leaf, you will need to manually initialize the package.
+
+```php
+Leaf\Anchor\CSRF::init();
+```
+
+This function generates a token with a secret and a random hash and saves that in a session. If no session exists, the CSRF module will create a session for your app and save the token in that session,
+
+### Config
+
+Just like every other leaf module, this module also allows you to customize it to behave in any way you want it to behave. Also, since this module is built on the Anchor module, the config object is shared with Anchor. To set any configuration, simply call the `config` method.
+
+**Available config:**
+
+- **SECRET_KEY** - This is the key with which the token is saved and used in your leaf app. If this is not specified, leaf uses the name `_token` as done in other frameworks like Laravel.
+
+- **SECRET** - This is the secret key used to encrypt the token. Leaf also has a default secret key set for you. Note that the secret key is attached to a set of unique numbers that not even leaf knows.
+
+- **EXCEPT** - This is an array of routes that you want to exclude from the CSRF protection.
+
+- **METHODS** - This is an array of HTTP methods to apply CSRF protection to. By default, leaf uses `["POST", "PUT", "PATCH", "DELETE"]`
+
+```php
+use Leaf\Anchor\CSRF;
+
+CSRF::config([
+ "METHODS" => ["GET"],
+ "EXCEPT" => ["/"],
+]);
+```
+
+## Token
+
+A token is generated under the hood for your application, you can get this token to submit in forms using the `token` method.
+
+```php
+$csrfToken = Leaf\Anchor\CSRF::token();
+
+>> ["_token" => "TOKEN VALUE"]
+```
+
+To make things a bit easier, `token` returns associative array holding the token key name and the token itself. This is an example JSON represenation.
+
+```json
+{"_token": "TOKEN VALUE"}
+```
+
+## Form
+
+You would usually want to append a hidden input field holding the token to a form so it doesn't fail the CSRF check. Although you can use the `token` method above to do just that, the `form` method makes it a lot easier as it renders the input field and populates it with the token.
+
+```php
+
+```
+
+## Functional Mode
+
+Just as with other modules, leaf csrf also ships with global functions that make development a lot easier.
+
+### _token
+
+This method returns the CSRF token just as done with the `token` method above.
+
+```php
+$csrfToken = _token();
+
+>> ["_token" => "TOKEN VALUE"]
+```
+
+### _csrfField
+
+This directly renders the form field for the CSRF token generated.
+
+```php
+
+```
diff --git a/apps/docs/src/modules/anchor/index.md b/apps/docs/src/modules/anchor/index.md
new file mode 100644
index 0000000..08bad4f
--- /dev/null
+++ b/apps/docs/src/modules/anchor/index.md
@@ -0,0 +1,67 @@
+# Leaf Anchor
+
+
+
+
+Leaf anchor is a new module which contains basic security features for the leaf framework. This module is actively being developed and will be updated whenever a security vulnerability is discovered.
+
+::: danger NOTE
+The whole of leaf 3 and some of it's modules rely on this particular module. In case of updates, you **might** need to update the packages that rely on this module to receive the security updates.
+:::
+
+## Installation
+
+::: warning
+There is no need to manually add the anchor module if you're using Leaf 3 since this is done for you automatically.
+:::
+
+You can quickly and simply install Leaf anchor through composer or the leaf cli.
+
+```bash
+composer require leafs/anchor
+```
+
+or with the leaf cli:
+
+```bash
+leaf install anchor
+```
+
+From there you can use the `Leaf\Anchor` class.
+
+## Base XSS protection
+
+Attackers pass executable scripts into your application through input fields, urls, ... These scripts are then executed and perform whatever action the attacker needs. To prevent this, you will need to sanitize your data to make sure PHP treats them as nothing more than text.
+
+The `sanitize` on leaf anchor handles all of this for you so you don't have to worry about escaping them.
+
+```php
+
+# 🧨 Leaf Authentication
+
+Authentication provides simple methods to help with manual authentication and working with tokens. In v2, Authentication has been added to Leaf Heplers, so it's now just an authentication helper. If you want a much simpler way, you can check out [Simple Auth](/modules/auth/).
+
+## Working with tokens
+
+Leaf provides you with the `JWT` object which includes various methods for creating and parsing token data....but we do not advice directly using the `JWT` object. For this reason, this object has been created to work with all the `JWT` data.
+
+```php
+use Leaf\Helpers\Authentication as Auth;
+
+$token = Auth::generateSimpleToken(...);
+```
+
+The examples below will all use `Auth::` pointing the example above.
+
+## Authentication methods
+
+Here are the methods you can use on `Authentication`
+
+### generateSimpleToken()
+
+This method generates a new JSON Web Token. It takes 2 arguments. check out JWT for more info on JWT params
+
+- (int) - A user id to encode
+- (string) - A secret phrase to encode with the token
+
+```php
+$token = Auth::generateSimpleToken(24', 'LEAF PHP SECRET CODE 1442');
+```
+
+### generateToken()
+
+This method generates a new JSON Web Token: the same as `generateSimpleToken`, the only difference is that this method gives you more control over the token i.e. expiry time and custom fields. This method takes in 2 params, the entire `payload` of your JWT and a secret phrase. check out JWT for more info on JWT payload params.
+
+- (array) - JWT payload
+- (string) - A secret phrase to encode with the token
+
+```php
+$payload = [
+ 'iat' => time(),
+ 'iss' => 'localhost',
+ 'exp' => time() + (15*60),
+ 'userId' => $user_id,
+ 'username' => $username
+];
+
+$token = Auth::generateToken($payload, "secret phrase");
+```
+
+### validateToken()
+
+This method is used to confirm the identity of a token from an authorization header.
+
+```php
+$payload = Auth::validateToken("SECRET PHRASE");
+```
+
+If the token is valid, it returns the decoded `payload`. So if we encode a payload:
+
+```php
+$payload = [
+ 'iat' => time(),
+ 'iss' => 'localhost',
+ 'exp' => time() + (15*60),
+ 'userId' => $user_id,
+ 'username' => $username
+];
+```
+
+We can retrieve this payload by calling
+
+```php
+$payload = Auth::validateToken("SECRET PHRASE");
+
+$username = $payload["username"];
+```
+
+If the validation fails, it returns `null`, you can get any error by calling the `errors` method.
+
+```php
+$payload = Auth::validateToken("SECRET PHRASE"); // returns null if failed
+
+if (!$payload) {
+ $response->exit(Auth::errors());
+}
+```
+
+### validate()
+
+This method is used to confirm the identity of a token. Unlike `validateToken`, the token to validate is directly passed into it. Just like `validateToken`, it returns the token's `payload`
+
+```php
+$payload = Auth::validate($token, "SECRET PHRASE");
+
+$username = $payload["username"];
+```
+
+If the validation fails, it returns `null`, you can get any error by calling the `errors` method.
+
+```php
+$payload = Auth::validate($token, "SECRET PHRASE"); // returns null if failed
+
+if (!$payload) {
+ $response->exit(Auth::errors());
+}
+```
+
+### getBearerToken()
+
+This method is used to get the bearer token from an authorization header. It returns `false` if there's no token. You can then use the `errors` method to retrieve any errors.
+
+```php
+$token = Auth::getBearerToken();
+
+// if there are any errors
+if (!$token) {
+ echo Auth::errors();
+}
+```
+
+### getAuthorizationHeader()
+
+This method is used to an authorization header. Returns `null` if anything goes wrong.
+
+```php
+$authHeader = Auth::getAuthorizationHeader();
+```
diff --git a/apps/docs/src/modules/auth/config.md b/apps/docs/src/modules/auth/config.md
new file mode 100755
index 0000000..a68d297
--- /dev/null
+++ b/apps/docs/src/modules/auth/config.md
@@ -0,0 +1,366 @@
+# Auth Config
+
+
+
+Auth Config was added to give you more control over how leaf handles authentication in your apps. Leaf Auth has been configured perfectly for most apps, but not all use cases are the same, hence, this brilliant addition.
+
+This includes various configurations for doing things like:
+
+- Configuring tokens
+- Hiding/Showing user fields
+- Adding/removing default timestamps
+- Changing the default password key
+- Password encoding and verification
+- Adding custom validation messages
+
+## Setting config values
+
+To set a config variable, you can simply call the `config` method.
+
+
+
+## Database Config
+
+Leaf Auth uses a database to store and retrieve users. It has been configured to work with common setups, but you can change the database config to suit your needs.
+
+### DB_TABLE
+
+The `DB_TABLE` config allows you to set a particular table which leaf auth will perform operations on. Leaf auth will use this database table for storing and retrieving users. By default, it is set to `users`. This allows you to login, signup, update and fetch users without explicitly adding a table each time.
+
+### USE_TIMESTAMPS
+
+This determines whether Leaf should add the default `created_at` and `updated_at` timestamps on register and update. Default is `true`.
+
+### TIMESTAMP_FORMAT
+
+If you set `USE_TIMESTAMPS` to `true` you can then use this property to specify the format that you want your timestamps to be saved in.
+Be aware that `auth` uses the `leafs/date` module, so the accepted formats are listed [here](/modules/date/#display)
+
+### PASSWORD_KEY
+
+This allows you to change the password field name, maybe yours is passcode? This tells leaf to look for a user's password in that field. The example below tells leaf to search for passwords in the `passcode` column. (the default field is password)
+
+
+
+### ID_KEY
+
+`ID_KEY` allows you to set your primary key name. For instance, you might have used `_id` instead of `id`. This setting allows you to quickly and effectively switch your key name.
+
+
+
+### USE_UUID
+
+This simply allows you to set the value for user ids on your own. This is done in order to add support for UUIDs in your registrations and not go with the default SQL increments.
+
+
+
+## Password Encoding
+
+These configuration options control how Leaf Auth encodes and verifies passwords. Leaf Auth uses `PASSWORD_DEFAULT` to encode passwords and `PASSWORD_VERIFY` to verify passwords.
+
+### PASSWORD_ENCODE
+
+This setting is used when leaf wants to encode a password. It uses the `Leaf\Helpers\Password::hash` method by default, but you can change this to suit your needs. It accepts these values:
+
+- `false` - This turns off password encoding
+- `null` - This uses the default encoding method (Leaf\Helpers\Password::hash)
+- `Password::MD5` - This uses md5. We're still keeping support for md5 :-) (Leaf\Helpers\Password::MD5)
+- `function` - This uses a custom method. The method should accept a password and return the encoded password.
+
+
+
+### PASSWORD_VERIFY
+
+This setting is used by Leaf Auth to verify whether a password is correct. It uses the `Leaf\Helpers\Password::verify` method by default, but you can change this to suit your needs. It accepts these values:
+
+- `false` - This turns off password verification
+- `null` - This uses the default verification method (Leaf\Helpers\Password::verify)
+- `Password::MD5` - This uses md5. We're still keeping support for md5 :-) (Leaf\Helpers\Password::MD5)
+- `function` - This uses a custom method. The method should accept a password and return the encoded password.
+
+This setting is called when Leaf tries to verify a password. It works just like `PASSWORD_ENCODE` above.
+
+
+
+## Special Config
+
+These configuration options control special features of Leaf Auth.
+
+### HIDE_ID
+
+This is a boolean which determines whether to hide the id in the user object returned on login/register. Default is `true`.
+
+### AUTH_NO_PASS
+
+This allows you to _manually_ tell leaf auth that no password is required for authentication. When this is set to true, leaf auth will assume there is no password and act accordingly. If there is no password field set in the credentials passed into the `login` or `register` methods, leaf auth will automatically set this to `true`.
+
+### HIDE_PASSWORD
+
+Just as the name implies, allows you to hide or show the password in the final results returned from auth. Default is `true`.
+
+## Error Message Config
+
+These configuration options control the error messages returned by Leaf Auth. You can change these to suit your needs.
+
+### LOGIN_PARAMS_ERROR
+
+This is the error to show if there's an error with any parameter which isn't the password eg: username:
+
+
+
+```php
+$auth->config('LOGIN_PARAMS_ERROR', 'Username is incorrect!');
+```
+
+
+
+
+```php
+auth()->config('LOGIN_PARAMS_ERROR', 'Username is incorrect!');
+```
+
+
+
+Default is `Incorrect credentials!`.
+
+### LOGIN_PASSWORD_ERROR
+
+This is the error to show if there's an error with the password.
+
+Default is `Password is incorrect!`.
+
+
+
+```php
+$auth->config('LOGIN_PASSWORD_ERROR', 'Password is incorrect!');
+```
+
+
+
+
+
+```php
+auth()->config('LOGIN_PASSWORD_ERROR', 'Password is incorrect!');
+```
+
+
+
+## Session Config
+
+These configuration options control how Leaf Auth handles sessions. You can change these to suit your needs.
+
+### USE_SESSION
+
+Use session based authentication instead of the default JWT based auth. Without this setting enbled, you can't use any of the session methods below. Default is `false`.
+
+### SESSION_ON_REGISTER
+
+If set to `true`, a session will be created on a successful registration. If set to `false`, sessions will only be created when a user successfully logs into their account. The default value for this config is `false`.
+
+### SESSION_REDIRECT_ON_LOGIN
+
+This configuration option determins whether to redirect to a page after login. When set to `true`, the options set in `GUARD_LOGIN`, `GUARD_REGISTER` and `GUARD_HOME` will be used to redirect the user to the right page based on their state. Default is `true`.
+
+### GUARD_LOGIN
+
+The page route. Default is `/auth/login`.
+
+### GUARD_REGISTER
+
+The register page route. Default is `/auth/register`.
+
+### GUARD_LOGOUT
+
+Logout route handler. Default is `/auth/logout`.
+
+### GUARD_HOME
+
+Home page route. Default is `/home`.
+
+### SESSION_LIFETIME
+
+This option allows you to set the lifetime of the session. After this time, the session will expire and the user will have to login again. Default is `1 day`. You can also set `SESSION_LIFETIME` to `0` to disable session expiration.
+
+### SESSION_COOKIE_PARAMS
+
+This option allows you to set the cookie params for the session. These are the defaults set for you:
+
+```php
+[
+ 'secure' => true,
+ 'httponly' => true,
+ 'samesite' => 'lax'
+]
+```
+
+### SAVE_SESSION_JWT
+
+Add an auth token to the auth session? This allows you save a generated JWT to the session. You might want to use this if you want to extend your app into an API. Default is `false`.
+
+## Token Config
+
+These configuration options control how Leaf Auth handles tokens. You can change these to suit your needs.
+
+### TOKEN_LIFETIME
+
+How long the token can be used before it expires. Default is 1 day.
+
+### TOKEN_SECRET
+
+This is the secret key used to generate tokens for users on signup and register.
+
+## Next Steps
+
+
diff --git a/apps/docs/src/modules/auth/helpers.md b/apps/docs/src/modules/auth/helpers.md
new file mode 100644
index 0000000..ccbcc31
--- /dev/null
+++ b/apps/docs/src/modules/auth/helpers.md
@@ -0,0 +1,137 @@
+# Helper Methods
+
+Leaf Auth already has methods that cater for most of your authentication use-cases, however, you may have a flow that is more complex than what Leaf Auth offers, for this reason, Leaf Auth provides low-level helper methods that allow you build auth flows from the ground up.
+
+## Working with JWTs
+
+According to the [JWT docs](https://jwt.io/introduction), JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
+
+In authentication, you can use JWTs as an identifier that a user has successfully logged in. The JWT usually also contains information about the authenticated user, usually the user's id. Leaf auth has helpers for interacting with JWTs. We'll take a look at them in this document.
+
+## Generating JWTs
+
+Leaf provides multiple ways for generating JWTs.
+
+- ### Using the `generateSimpleToken` method
+
+ The simplest method is to use the `generateSimpleToken` method on the `Leaf\Helpers\Authentication` class. This method takes in 3 parameters:
+
+ - The user id of the currently authenticated user
+ - The jwt secret string
+ - The jwt expiry time in seconds
+
+ ```php
+ use Leaf\Helpers\Authentication;
+
+ // ... your application code
+
+ Authentication::generateSimpleToken(
+ $userId,
+ 'MY_JWT_SECRET_STRING',
+ time() + 3600
+ );
+ ```
+
+- ### Using the `generateToken` method
+
+ The `generateToken` method on the `Leaf\Helpers\Authentication` class is a more advanced method for generating JWTs. It takes in 2 parameters:
+
+ - An array of claims to be added to the JWT
+ - The JWT secret string
+
+ ```php
+ use Leaf\Helpers\Authentication;
+
+ // ... your application code
+
+ Authentication::generateToken(
+ [
+ 'user_id' => $userId,
+ 'iat' => time(),
+ 'iss' => 'localhost',
+ 'exp' => time() + 3600,
+ ],
+ 'MY_JWT_SECRET_STRING'
+ );
+ ```
+
+## Getting JWTs from the request
+
+Tokens from authenticated requests are usually sent in the `Authorization` header. Leaf Auth provides helpers that allow you directly pull in tokens from the request header.
+
+### Using the `getAuthorizationHeader` method
+
+Leaf Auth provides the `getAuthorizationHeader` helper method for getting the token from the request header. It returns null if no authorization header is found.
+
+```php
+use Leaf\Helpers\Authentication;
+
+$header = Authentication::getAuthorizationHeader();
+```
+
+### Using the `getBearerToken` method
+
+Leaf Auth provides the `getBearerToken` helper method for getting the token from the request header. It returns null if no bearer token is found.
+
+```php
+use Leaf\Helpers\Authentication;
+
+$token = Authentication::getBearerToken();
+```
+
+## Verifying JWTs
+
+Verifying JWTs is a very important step in the authentication process. Tokens can be tampered with, they can also expire, or be issued by an untrusted source. For this reason, you should always verify the token before using it.
+
+Leaf Auth provides 2 methods for verifying JWTs.
+
+- ### Using the `validateToken` method
+
+This method automatically checks if there is a token in the request header, and if there is, it verifies the token. It returns the decoded token if the token is valid, and returns false if the token is invalid.
+
+It takes in a single parameter, which is the JWT secret string. This secret string should be the same as the one used to generate the token.
+
+```php
+use Leaf\Helpers\Authentication;
+
+$data = Authentication::validateToken($secret);
+```
+
+Note that this method will automatically return false if there is no token in the request header. You can use the `errors` method to get the error message which is why the token validation failed.
+
+```php
+use Leaf\Helpers\Authentication;
+
+$data = Authentication::validateToken($secret);
+
+if (!$data) {
+ $errors = Authentication::errors();
+}
+```
+
+- ### Using the `validate` method
+
+Unlike the `validateToken` method, this method does not automatically check for a token in the request header. It takes in 2 parameters:
+
+- The token to be validated
+- The JWT secret string
+
+```php
+use Leaf\Helpers\Authentication;
+
+$token = Authentication::getBearerToken();
+$data = Authentication::validate($token, $secret);
+```
+
+Just like the `validateToken` method, this method returns the decoded token if the token is valid, and returns false if the token is invalid. You can use the `errors` method to get the error message which is why the token validation failed.
+
+```php
+use Leaf\Helpers\Authentication;
+
+$token = Authentication::getBearerToken();
+$data = Authentication::validate($token, $secret);
+
+if (!$data) {
+ $errors = Authentication::errors();
+}
+```
diff --git a/apps/docs/src/modules/auth/index.md b/apps/docs/src/modules/auth/index.md
new file mode 100755
index 0000000..4547944
--- /dev/null
+++ b/apps/docs/src/modules/auth/index.md
@@ -0,0 +1,265 @@
+# Authentication
+
+
+
+Numerous web applications offer their users a means to authenticate and access the application by "logging in." Adding this functionality to web applications can be a challenging and potentially dangerous task. For this reason, Leaf provides a lightweight but very powerful authentication system known as Leaf Auth.
+
+Leaf Auth gives you clean and simple functions to handle complex authentication flows in a few lines of code. It is customizable to the core and allows for a bunch of configuration options that determine how it handles authentication in general.
+
+You can still handle authentication without using Leaf Auth, however, Leaf Auth is a more secure and reliable way to handle authentication in your apps.
+
+## Installing Leaf Auth
+
+You can install Leaf Auth using the Leaf CLI:
+
+```bash
+leaf install auth
+```
+
+Or with composer:
+
+```bash
+composer require leafs/auth
+```
+
+From there, you can link your database and start writing some awesome queries.
+
+## Database Considerations
+
+Leaf Auth doesn't give you any structure for your database, with that, you can structure your database in any way you prefer. However, there are some things you should note:
+
+### Database primary key
+
+By default, Leaf Auth assumes that your database primary key is `id`. If however, you have a database where you are using another field, say `admin_id` as the primary key, you will need to tell Leaf Auth the name of your primary key. You can do this using the `ID_KEY` config:
+
+
+
+### Database table
+
+By default, Leaf Auth assumes that you will save your users in a database table named `users`, this might however not be the case for your application. If you want to use a different table, you can configure Leaf Auth using `DB_TABLE`:
+
+
+
+## Database Connection
+
+After installing leaf auth, you would need to connect to a database. Leaf auth will search for users and add/update users in this database when a login/register or update operation is called. There are a couple of ways to connect to a database with leaf auth.
+
+### Manual Connection
+
+Leaf Auth provides a `connect()` method that allows you to connect to your database by passing in your database connection parameters. This is the most basic and straightforward way to connect to your database.
+
+
+
+### Auto Connect
+
+Leaf Auth comes with an `autoConnect()` method that allows you to connect to your database using your environment variables. Most MVC frameworks and other libraries rely on a `.env` file for a lot of configuration including the database. With `autoConnect`, you can directly pick up these configs and create a connection from them.
+
+**example env:**
+
+```env
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=LeafMVC
+DB_USERNAME=root
+DB_PASSWORD=
+```
+
+Based on the example above, you can connect to your database using:
+
+
+
+```php
+$auth = new Leaf\Auth;
+$auth->autoConnect();
+```
+
+
+
+
+
+```php
+auth()->autoConnect();
+```
+
+
+
+### PDO connection
+
+Leaf Auth also allows you to skip the entire connection process and share an existing PDO instance with leaf db. This allows you to gradually rewrite your existing apps with Leaf Auth without having multiple db connections and doing so at your own pace.
+
+
+
+```php
+$db = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
+$auth = new Leaf\Auth;
+
+$auth->dbConnection($db);
+
+// you can use leaf auth the same way you always have
+```
+
+
+
+
+
+```php
+$db = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
+
+auth()->dbConnection($db);
+
+// you can use leaf auth the same way you always have
+```
+
+
+
+Leaf Db has been rewritten based on PDO, this also means that you can pass your leaf db connection into leaf auth directly.
+
+
+
+```php
+auth()->dbConnection(db()->connection());
+```
+
+### Leaf db (auth v2 + leaf 3 only)
+
+If you are using leaf auth in a leaf 3 app, you will have access to the auth global as shown in some of the above connections. Along with this, if you already have a leaf db connection, you no longer need to explicitly connect to your database. Leaf auth searches for a leaf db instance and connects to it automatically.
+
+```php
+connect('127.0.0.1', 'dbname', 'username', 'password');
+
+// you can use auth straight away without any connect
+auth()->login(...);
+```
+
+## Functional Mode
+
+If you are using leaf auth v2 in a leaf 3 app, you will have access to the `auth` global which allows you to use Leaf Auth from anywhere in your entire application. You simply need to call `auth()` and leaf 3 will create and maintain a shared instance of Leaf auth which you can call from anywhere.
+
+This also means that you don't need to initialize leaf auth anymore.
+
+```php
+autoConnect();
+
+app()->get("/", function () {
+ // auth can be used here
+ // auth()->...
+});
+
+app()->run();
+```
+
+Functional mode also makes the `guard`, `hasAuth` and `sessionUser` globals available to you from anywhere.
+
+### guard
+
+The guard method is a shortcut method for `Auth::guard()`. You can find the guards documentation [here](/modules/auth/session.html#guard).
+
+### hasAuth
+
+`hasAuth` returns a boolean which is whether there's an active user session or not.
+
+### sessionUser
+
+This method returns the active session user or null if there's no session user.
+
+
diff --git a/apps/docs/src/modules/auth/login.md b/apps/docs/src/modules/auth/login.md
new file mode 100644
index 0000000..45d1260
--- /dev/null
+++ b/apps/docs/src/modules/auth/login.md
@@ -0,0 +1,387 @@
+# Login
+
+
+
+
+Building a login system is one of the most common tasks in web development, it can also be one of the most tedious. Leaf Auth provides a flexible and secure authentication system for your web apps and APIs that is simple and easy to use.
+
+## Auth Systems
+
+Leaf Auth provides two authentication systems:
+
+- Token based authentication
+- Session based authentication
+
+These two systems are very similar, the only difference is that token based authentication uses tokens to authenticate users while session based authentication uses sessions to authenticate users. *Token based auth is used by default, but you can switch to session based authentication using the [Auth Config](/modules/auth/config#use-session).*
+
+### Token based authentication
+
+Token based authentication is a system where a user is given a token upon login. This token is then used to authenticate the user on every request. This is the most common authentication system for APIs.
+
+
+ New to Token Authentication?
+
+
+
+### Session based authentication
+
+Session based authentication is a system where a user is given a session upon login. This session is then used to authenticate the user on every request. This is the most common authentication system for web apps.
+
+
+ New to Session Authentication?
+
+
+
+## The login method
+
+Leaf auth provides a `login()` method used to authenticate users and create a session or token for them. The `login()` method takes in an array of data you want to use to authenticate the user. This data is usually the user's email and password.
+
+
+
+This example uses the user's email and password to authenticate them.
+
+If the user is authenticated, a session or token is created for them and the user is returned. If the user is not authenticated, `null` is returned instead. You can use this to check if the user is authenticated or not.
+
+
+
+```php
+$auth = new Leaf\Auth;
+$data = $auth->login([
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+} else {
+ // user is not authenticated
+}
+```
+
+
+
+
+
+```php
+$data = auth()->login([
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+} else {
+ // user is not authenticated
+}
+```
+
+
+
+To get the reason why the user is not authenticated, you can use the `errors()` method. This returns an array of errors that occured during authentication.
+
+
+
+```php{11}
+$auth = new Leaf\Auth;
+$data = $auth->login([
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+} else {
+ // user is not authenticated
+ $errors = $auth->errors();
+}
+```
+
+
+
+
+
+```php{10}
+$data = auth()->login([
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+} else {
+ // user is not authenticated
+ $errors = auth()->errors();
+}
+```
+
+
+
+If the authentication was successful, the user is returned. You can use this to get the user's data.
+
+
+
+```php{9,10}
+$auth = new Leaf\Auth;
+$data = $auth->login([
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+ $token = $data['token'];
+ $user = $data['user'];
+} else {
+ // user is not authenticated
+ $errors = $auth->errors();
+}
+```
+
+
+
+
+
+```php{8,9}
+$data = auth()->login([
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+ $token = $data['token'];
+ $user = $data['user'];
+} else {
+ // user is not authenticated
+ $errors = auth()->errors();
+}
+```
+
+
+
+## Normalizing user data
+
+The data from a successful login looks something like this:
+
+```php
+[
+ 'user' => [
+ 'username' => 'mychi.darko',
+ 'email' => 'mychi@leafphp.dev',
+ 'created_at' => '2019-09-20 13:47:48'
+ ],
+ 'token' => 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NzYxMzUzMjgsImlzcyI6ImxvY2FsaG9zdCIsImV4cCI6MTU3NjEzNjIyOCwidXNlcklkIjoxfQ.7FODXGGJKioGQVX4ic0DJLoMIQTVUlsd4zFAJA4DAkg'
+]
+```
+
+Even without knowing what our database look like, you can tell that the user ID and password are not included in the data. This is because Leaf Auth does not return sensitive data like passwords and user IDs. This is done to prevent sensitive data from being exposed.
+
+In some cases, you might need the user id returned at login. To do this, you need to configure Leaf Auth to expose the user id. You can do this by setting the `HIDE_ID` config to `false`.
+
+
+
+## Session based auth
+
+Leaf Auth uses token based authentication by default, but allows you to seamlessly switch to session based authentication by setting the `USE_SESSION` config to `true` or by using the `useSession()` method.
+
+
+
+Just like with token based authentication, you can use the `login()` method to authenticate users. The only difference is that the `login()` method creates a session for your user instead of just returning the user info and a token. It also automatically redirects you to a route defined as `GUARD_HOME` if you have the `SESSION_REDIRECT_ON_LOGIN` config set to `true`.
+
+
+
+```php
+$auth = new Leaf\Auth;
+
+$auth->useSession();
+$auth->config('GUARD_HOME', '/home');
+
+// will automatically redirect to /home if successful
+$data = $auth->login([
+ 'email' => $email,
+ 'password' => $password,
+]);
+
+if (!$data) {
+ // you can pass the auth errors into a view
+ return $template->render('pages.auth.login', [
+ 'errors' => auth()->errors(),
+ 'email' => $email,
+ 'password' => $password,
+ ]);
+}
+```
+
+
+
+
+
+```php
+auth()->useSession();
+auth()->config('GUARD_HOME', '/home');
+
+// will automatically redirect to /home if successful
+$user = auth()->login([
+ 'email' => $email,
+ 'password' => $password
+]);
+
+if (!$user) {
+ // you can pass the auth errors into a view
+ return $template->render('pages.auth.login', [
+ 'errors' => auth()->errors(),
+ 'email' => $email,
+ 'password' => $password,
+ ]);
+}
+```
+
+
+
+For more information on session based authentication, check out the [session based authentication](/modules/auth/session) page.
+
+## Session lifetime
+
+Session lifetime is the amount of time a session is valid for. This is usually set to a few minutes or hours. Leaf Auth allows you to set the session lifetime using the `SESSION_LIFETIME` config. This config is set to `1 day` by default.
+
+You can set the session lifetime using seconds or a string that can be parsed by the [PHP `strtotime()`](https://www.php.net/manual/en/function.strtotime.php) function.
+
+
+
+## Token lifetime
+
+Token lifetime is the amount of time a token is valid for. This is usually set to a few minutes or hours. Leaf Auth allows you to set the token lifetime using the `TOKEN_LIFETIME` config.
+
+
+
+## Password Encoding
+
+Leaf Auth uses the [Leaf Password Helper](/modules/password/) to encode passwords. It supports the most popular password encoding algorithms including `bcrypt`, `argon2i` and `md5`. You can still use your own password encoder by updating the [`PASSWORD_VERIFY`](/modules/auth/config.html#password-verify) config.
+
+::: tip Custom Password Encoder
+In case you want to use your own password verification method, your method must return `true` if the password is correct and `false` if the password is incorrect.
+:::
diff --git a/apps/docs/src/modules/auth/mvc.md b/apps/docs/src/modules/auth/mvc.md
new file mode 100644
index 0000000..d58865c
--- /dev/null
+++ b/apps/docs/src/modules/auth/mvc.md
@@ -0,0 +1,17 @@
+# Usage with Leaf MVC
+
+Leaf MVC and Leaf API both come with built-in support for Leaf Auth. This means you can skip the initial setup and get right into using Leaf Auth. To get started, head over to your `public/index.php` file and uncomment the following line:
+
+```php
+\Leaf\Database::initDb();
+```
+
+This will allow Leaf Auth to use your database connection set up in your `.env` file. If you don't have a database connection set up, you can set one up by following the [Leaf MVC Database docs](/docs/leafmvc/).
+
+## Auth config
+
+Once your database connection is set up, you can pretty much start using Leaf Auth. However, you can also set up your auth config in your `config/auth.php` file. This allows you to control Leaf Auth's behaviour. You can find a list of all available config options [here](/modules/auth/config).
+
+## Usage
+
+From this point on, you can use Leaf Auth as you would normally. You can find the full docs [here](/modules/auth/).
diff --git a/apps/docs/src/modules/auth/protecting-your-routes.md b/apps/docs/src/modules/auth/protecting-your-routes.md
new file mode 100644
index 0000000..fa854ab
--- /dev/null
+++ b/apps/docs/src/modules/auth/protecting-your-routes.md
@@ -0,0 +1,297 @@
+# Protected Routes
+
+One of the most common use cases for authentication is protecting certain routes from being accessed by unauthorized users. Most authentication systems use a "guard" to authenticate and authorize users. While this is good, Leaf tries as much as possible to make your life easier by providing a simpler way to deal with protected routes in your app.
+
+## The `user` method
+
+The `user()` method is a simple way to check if a user is logged in. It returns the currently logged in user if an authenticated user is found and `null` if a user is not logged in.
+
+This works for both session and token based authentication. In case of token based authentication, Leaf Auth will also check if the token is valid. If it is, the user is returned, if not, `null` is returned. You can get the reason for the authentication failure by calling the `errors()` method.
+
+
+
+```php{3,8}
+$auth = new Leaf\Auth;
+
+$user = $auth->user();
+
+if ($user) {
+ // user is logged in
+} else {
+ // user is not logged in
+ $errors = $auth->errors();
+}
+```
+
+
+
+
+
+```php{1,7}
+$user = auth()->user();
+
+if ($user) {
+ // user is logged in
+} else {
+ // user is not logged in
+ $errors = auth()->errors();
+}
+```
+
+
+
+Using this method, you can easily protect your routes by checking if a user is logged in. If a user is not logged in, you can redirect them to the login page or return a 401 error. Here's an example:
+
+
+
+```php
+$app->get('/protected', function () use($app, $auth) {
+ $user = $auth->user();
+
+ if ($user) {
+ // user is logged in
+ } else {
+ // user is not logged in
+ $app->response()->redirect('/login');
+ }
+});
+```
+
+
+
+
+
+```php
+app()->get('/protected', function () {
+ $user = auth()->user();
+
+ if ($user) {
+ // user is logged in
+ } else {
+ // user is not logged in
+ response()->redirect('/login');
+ }
+});
+```
+
+
+
+For API routes, you can return a 401 error if a user is not logged in.
+
+
+
+```php
+$app->get('/protected', function () use($app, $auth) {
+ $user = $auth->user();
+
+ if ($user) {
+ // user is logged in
+ } else {
+ // user is not logged in
+ $app->response()->json([
+ "error" => "Unauthorized",
+ "data" => $auth->errors(),
+ ], 401);
+ }
+});
+```
+
+
+
+
+
+```php
+app()->get('/protected', function () {
+ $user = auth()->user();
+
+ if ($user) {
+ // user is logged in
+ } else {
+ // user is not logged in
+ response()->json([
+ "error" => "Unauthorized",
+ "data" => auth()->errors(),
+ ], 401);
+ }
+});
+```
+
+
+
+## The `id` method
+
+The `id()` method returns the id of the currently logged in user. This is useful when you need to get the id of the currently logged in user.
+
+It works exactly like the `user()` method above, except it returns the id of the user instead of the user object.
+
+
+
+```php
+$app->get('/protected', function () use($app, $auth) {
+ $id = $auth->id();
+
+ if ($id) {
+ // user is logged in
+ } else {
+ // user is not logged in
+ $app->response()->redirect('/login');
+ }
+});
+```
+
+
+
+
+
+```php
+app()->get('/protected', function () {
+ $id = auth()->id();
+
+ if ($id) {
+ // user is logged in
+ } else {
+ // user is not logged in
+ response()->redirect('/login');
+ }
+});
+```
+
+
+
+## Using middleware
+
+Leaf allows you to define behaviour for your routes using middleware. The latest update to the Leaf Router allows you to define named middleware. This means you can define a middleware once and use it on multiple routes.
+
+Using named middleware, you can easily protect your routes by defining a middleware that checks if a user is logged in and use it on the routes/groups you want to protect.
+
+
+
+```php
+$app->registerMiddleware('auth', function () use($app, $auth) {
+ $user = $auth->user();
+
+ if (!$user) {
+ // user is not logged in
+ $app->response()->exit([
+ 'error' => 'Unauthorized',
+ 'data' => $auth->errors(),
+ ], 401);
+ }
+});
+
+$app->get('/protected', ['middleware' => 'auth', function () use($app, $auth) {
+ // user is logged in
+}]);
+
+$app->group(['middleware' => 'auth', function () use($app, $auth) {
+ $app->get('/protected', function () use($app, $auth) {
+ // user is logged in
+ });
+
+ $app->get('/protected2', function () use($app, $auth) {
+ // user is logged in
+ });
+}]);
+```
+
+
+
+
+
+```php
+app()->registerMiddleware('auth', function () {
+ $user = auth()->user();
+
+ if (!$user) {
+ // user is not logged in
+ response()->exit([
+ 'error' => 'Unauthorized',
+ 'data' => auth()->errors(),
+ ], 401);
+ }
+});
+
+app()->get('/protected', ['middleware' => 'auth', function () {
+ // user is logged in
+}]);
+
+app()->group('/group', ['middleware' => 'auth', function () {
+ app()->get('/protected', function () {
+ // user is logged in
+ });
+
+ app()->get('/protected2', function () {
+ // user is logged in
+ });
+}]);
+```
+
+
+
+Using this method, you can easily define custom behaviour for your protected routes without having to repeat yourself.
+
+## Session Guard
+
+All the methods above work for both session and token based authentication. However, Leaf Auth also provides a session guard that automatically handles redirects and other session based authentication behaviour.
+
+The `guard()` method has 2 middleware provided automatically: `guest` and `auth`.
+
+The `guest` middleware checks if a user is logged in. If a user is logged in, they are redirected to the `GUARD_HOME` page. If a user is not logged in, they are allowed to continue.
+
+The `auth` middleware checks if a user is logged in. If a user is logged in, they are allowed to continue. If a user is not logged in, they are redirected to the `GUARD_LOGIN` page.
+
+
+
+```php
+$auth = new Leaf\Auth;
+
+$auth->config([
+ 'GUARD_HOME' => '/dashboard',
+ 'GUARD_LOGIN' => '/login',
+]);
+
+$app->get('/protected', function () use($app, $auth) {
+ // will redirect to /login if user is not logged in
+ $auth->guard('auth');
+
+ // user is logged in
+});
+
+$app->get('/login', function () use($app, $auth) {
+ // will redirect to /dashboard if user is logged in
+ $auth->guard('guest');
+
+ // user is not logged in
+});
+```
+
+
+
+
+
+```php
+auth()->config([
+ 'GUARD_HOME' => '/dashboard',
+ 'GUARD_LOGIN' => '/login',
+]);
+
+app()->get('/protected', function () {
+ // will redirect to /login if user is not logged in
+ auth()->guard('auth');
+
+ // user is logged in
+});
+
+app()->get('/login', function () {
+ // will redirect to /dashboard if user is logged in
+ auth()->guard('guest');
+
+ // user is not logged in
+});
+```
+
+
+
+## Next Steps
+
+There are still a few more useful session based authentication methods that Leaf Auth provides. You can check them out in the [Leaf Auth Session docs](/modules/auth/session).
diff --git a/apps/docs/src/modules/auth/session.md b/apps/docs/src/modules/auth/session.md
new file mode 100644
index 0000000..6de48fe
--- /dev/null
+++ b/apps/docs/src/modules/auth/session.md
@@ -0,0 +1,104 @@
+# Session support
+
+
+
+We've covered session support and it's use in various techniques like login, register, etc. But there are still a bunch of session auth methods that we haven't covered yet. Let's get into them.
+
+## Enabling sessions
+
+To get started with session support, just set the `USE_SESSION` config to true.
+
+```php
+auth()->config('USE_SESSION', true);
+```
+
+A much simpler way would be to simply call the `useSession` method.
+
+```php
+auth()->useSession();
+```
+
+## guard
+
+We covered guards in-depth in the [protecting your routes](/modules/auth/protecting-your-routes) section. You can use guards to check if a user is logged in or not.
+
+```php
+auth()->guard('auth');
+
+// or
+
+Leaf\Auth::guard('auth');
+
+// guest route redirects to home
+// route if you're logged in
+Leaf\Auth::guard('guest');
+```
+
+::: tip The guard method
+You can directly run a guard on the `guard` method.
+
+```php
+guard('guest');
+```
+
+:::
+
+## length
+
+With `length()`, you can get how long a user has been logged in. You can save the session time logs to your database in order to track users' login logs. The available logs are `SESSION_STARTED_AT` and `SESSION_LAST_ACTIVITY` which are automatically tracked by Leaf.
+
+```php
+$sessionDuration = auth()->length();
+```
+
+## lastActive
+
+`lastActive` allows you to get how much time has passed since the last session activity.
+
+```php
+$userLastSeen = auth()->lastActive();
+```
+
+## refresh
+
+As the name implies, you can refresh the session with this method. Refreshing sort of restarts the session, but you can keep the user's old session data if you wish to.
+
+```php
+if ($newAccountAdded) {
+ // will delete old session data
+ Leaf\Auth::refresh();
+} else {
+ // will keep session data
+ auth()->refresh(false);s
+}
+```
+
+## status
+
+::: danger Deprecated
+This method is deprecated and will be removed in the next major release. Use `user()` instead.
+:::
+
+`status` checks whether a user session is ongoing by looking for keys specific to Leaf session auth so it doesn't confuse a Leaf auth session with user defined sessions. Returns the user if a session is found and false if there's no session found.
+
+```php
+if (auth()->status()) {
+ return 'logged in';
+} else {
+ return 'guest mode';
+}
+```
+
+## logout
+
+Of course we'll need a method to logout/end our session. This is just the method for that.
+
+```php
+auth()->logout();
+```
+
+You can also pass in a route to redirect to after logging out.
+
+```php
+auth()->logout('/home');
+```
diff --git a/apps/docs/src/modules/auth/signup.md b/apps/docs/src/modules/auth/signup.md
new file mode 100644
index 0000000..7f4f4ee
--- /dev/null
+++ b/apps/docs/src/modules/auth/signup.md
@@ -0,0 +1,421 @@
+# Register
+
+
+
+
+Leaf Auth provides a flexible and secure authentication system for your web apps and APIs that is simple and easy to use. It allows you to quickly create a complete signup system for your app.
+
+## Auth Systems
+
+Leaf Auth provides two authentication systems:
+
+- Token based authentication
+- Session based authentication
+
+These two systems are very similar, the only difference is that token based authentication uses tokens to authenticate users while session based authentication uses sessions to authenticate users. *Token based auth is used by default, but you can switch to session based authentication using the [Auth Config](/modules/auth/config#use-session).*
+
+### Token based authentication
+
+Token based authentication is a system where a user is given a token upon login. This token is then used to authenticate the user on every request. This is the most common authentication system for APIs.
+
+
+ New to Token Authentication?
+
+
+
+### Session based authentication
+
+Session based authentication is a system where a user is given a session upon login. This session is then used to authenticate the user on every request. This is the most common authentication system for web apps.
+
+
+ New to Session Authentication?
+
+
+
+::: tip Defaults
+Token based auth is used by default, but you can switch to session based authentication using the [Auth Config](/modules/auth/config#use-session).
+:::
+
+## The register method
+
+Leaf auth provides a `register()` method used to sign users up and create a session or token for them. The `register()` method takes in an array of data you want to use to authenticate the user and a list of items that should be unique to users, like email and username.
+
+
+
+This example creates a new user with the username `example`, email `m@example.com` and password `password` in your database. The `register()` method then returns the user's data or session if the registration was successful. If the registration was not successful, `null` is returned instead.
+
+
+
+```php
+$auth = new Leaf\Auth;
+$data = $auth->register([
+ 'username' => 'example',
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+} else {
+ // user is not authenticated
+}
+```
+
+
+
+
+
+```php
+$data = auth()->register([
+ 'username' => 'example',
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+} else {
+ // user is not authenticated
+}
+```
+
+
+
+To get the reason why the user is not authenticated, you can use the `errors()` method. This returns an array of errors that occured during authentication.
+
+
+
+```php{12}
+$auth = new Leaf\Auth;
+$data = $auth->register([
+ 'username' => 'example',
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+} else {
+ // user is not authenticated
+ $errors = $auth->errors();
+}
+```
+
+
+
+
+
+```php{11}
+$data = auth()->register([
+ 'username' => 'example',
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+} else {
+ // user is not authenticated
+ $errors = auth()->errors();
+}
+```
+
+
+
+If the authentication was successful, the user is returned. You can use this to get the user's data.
+
+
+
+```php{10,11}
+$auth = new Leaf\Auth;
+$data = $auth->register([
+ 'username' => 'example',
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if ($data) {
+ // user is authenticated
+ $token = $data['token'];
+ $user = $data['user'];
+} else {
+ // user is not authenticated
+ $errors = $auth->errors();
+}
+```
+
+
+
+## Unique items
+
+The `register()` method takes in a list of items that should be unique to users. This is used to check if the user already exists in the database. If the user already exists, the `register()` method returns `null` and the reason why the user is not authenticated can be gotten using the `errors()` method.
+
+
+
+```php{12}
+$auth = new Leaf\Auth;
+$data = $auth->register([
+ 'username' => 'example',
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+], ['username', 'email']);
+
+if ($data) {
+ // user is authenticated
+} else {
+ // user is not authenticated
+ $errors = $auth->errors();
+}
+```
+
+
+
+
+
+```php{11}
+$data = auth()->register([
+ 'username' => 'example',
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+], ['username', 'email']);
+
+if ($data) {
+ // user is authenticated
+} else {
+ // user is not authenticated
+ $errors = auth()->errors();
+}
+```
+
+
+
+The code above checks if someone with the username `example` or email `m@example.com` already exists in the database. If they do, the `register()` method returns `null` and the reason why the user is not authenticated can be gotten using the `errors()` method.
+
+## Normalizing user data
+
+The data from a successful sign up looks something like this:
+
+```php
+[
+ 'user' => [
+ 'username' => 'example',
+ 'email' => 'm@example.com',
+ 'created_at' => '2019-09-20 13:47:48'
+ ],
+ 'token' => 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NzYxMzUzMjgsImlzcyI6ImxvY2FsaG9zdCIsImV4cCI6MTU3NjEzNjIyOCwidXNlcklkIjoxfQ.7FODXGGJKioGQVX4ic0DJLoMIQTVUlsd4zFAJA4DAkg'
+]
+```
+
+The user ID and password are not included in the data. This is because Leaf Auth does not return sensitive data like passwords and user IDs. This is done to prevent sensitive data from being exposed.
+
+In some cases, you might need the user id returned at sign up. To do this, you need to configure Leaf Auth to expose the user id. You can do this by setting the `HIDE_ID` config to `false`.
+
+
+
+## Password Encoding
+
+Leaf Auth uses the [Leaf Password Helper](/modules/password/) to encode passwords. It supports the most popular password encoding algorithms including `bcrypt`, `argon2i` and `md5`. You can still use your own password encoder by updating the [`PASSWORD_ENCODE`](/modules/auth/config.html#password-encode) config.
+
+::: tip Custom Password Encoder
+In case you want to use your own password encoder, your method must return the encoded password.
+:::
+
+## Session based auth
+
+Leaf Auth uses token based authentication by default, but allows you to seamlessly switch to session based authentication by setting the `USE_SESSION` config to `true` or by using the `useSession()` method.
+
+
+
+Just like with token based authentication, you can use the `register()` method to authenticate users. The only difference is that the `register()` method redirects you to a route defined as `GUARD_HOME` with a new session or redirects to `GUARD_LOGIN` if you have the `SESSION_ON_REGISTER` config set to `false`.
+
+
+
+```php
+$auth = new Leaf\Auth;
+
+$auth->useSession();
+$auth->config('GUARD_HOME', '/home');
+
+// will automatically redirect to /home if successful
+$user = $auth->register([
+ 'username' => 'example',
+ 'email' => 'm@example.com',
+ 'password' => 'password'
+]);
+
+if (!$user) {
+ // you can pass the auth errors into a view
+ return $template->render('pages.auth.login', [
+ 'errors' => auth()->errors(),
+ 'username' => $username,
+ 'password' => $password,
+ ]);
+}
+```
+
+
+
+
+
+```php
+auth()->useSession();
+auth()->config('GUARD_HOME', '/home');
+
+// will automatically redirect to /home if successful
+$user = auth()->register([
+ 'username' => $username,
+ 'email' => $email,
+ 'password' => $password
+]);
+
+if (!$user) {
+ // you can pass the auth errors into a view
+ return $template->render('pages.auth.register', [
+ 'errors' => auth()->errors(),
+ 'username' => $username,
+ 'email' => $email,
+ 'password' => $password,
+ ]);
+}
+```
+
+
+
+## Session on register
+
+Some authentication systems require you to log in right after creating an account. This means that you won't start a session until the user logs in. You can implement this by setting the `SESSION_ON_REGISTER` config to `false`.
+
+
+
+```php
+$auth = new Leaf\Auth;
+
+$auth->useSession();
+
+// set your login route...default is /auth/login
+$auth->config('GUARD_LOGIN', '/login');
+
+// set your home route...default is /home
+$auth->config('GUARD_HOME', '/dashboard');
+
+// Redirect to GUARD_LOGIN after auth
+$auth->config('SESSION_ON_REGISTER', false);
+
+// Login automatically after registration and redirect to GUARD_HOME
+$auth->config('SESSION_ON_REGISTER', true);
+```
+
+
+
+
+
+```php
+auth()->useSession();
+
+// set your login route...default is /auth/login
+auth()->config('GUARD_LOGIN', '/login');
+
+// set your home route...default is /home
+auth()->config('GUARD_HOME', '/dashboard');
+
+// Redirect to GUARD_LOGIN after auth
+auth()->config('SESSION_ON_REGISTER', false);
+
+// Login automatically after registration and redirect to GUARD_HOME
+auth()->config('SESSION_ON_REGISTER', true);
+```
+
+
diff --git a/apps/docs/src/modules/auth/update.md b/apps/docs/src/modules/auth/update.md
new file mode 100755
index 0000000..94f8556
--- /dev/null
+++ b/apps/docs/src/modules/auth/update.md
@@ -0,0 +1,128 @@
+# User update
+
+In addition to the `register()` and `login()` methods, Leaf Auth also provides a `update()` method which allows you to update a user's information. This method takes in 2 parameters:
+
+- The data to update
+- Unique values (optional)
+
+
+
+```php
+// data to update
+$data = $app->request()->get(['username', 'email']);
+$user = $auth->update($data);
+```
+
+
+
+
+
+```php
+// data to update
+$data = request()->get(['username', 'email']);
+$user = auth()->update($data);
+```
+
+
+
+The example above updates the user's username and email with the values passed in the request. Leaf Auth automatically gets the user id from the session or token and updates the user with the data passed. This means that there should be a session or token present in the request.
+
+If there is no session or token present in the request, the `update()` method will return `null`. You can get the errors using the `errors()` method and handle it however you want.
+
+
+
+## Unique values
+
+Just like the `register()` method, the `update()` method also takes in a second parameter which is an array of unique values. This is used to check if the user's new credentials are unique. If the user's new credentials are not unique, the `update()` method will return `null` and you can get the errors using the `errors()` method. Note that unique values in the `update()` method exclude the current user.
+
+
+
+## Session support
+
+When a user is updated in the database, the change is also reflected in the active session and the updated user is also returned. This means that you can use the `update()` method to update a user's information and also update the session with the new information. *Just be sure to turn on session based auth in the auth config.*
+
+
diff --git a/apps/docs/src/modules/auth/v/1/index.md b/apps/docs/src/modules/auth/v/1/index.md
new file mode 100755
index 0000000..8ed3ead
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/1/index.md
@@ -0,0 +1,775 @@
+---
+title: "Auth v1"
+---
+
+
+# ✨ Leaf Simple Auth
+
+Simple auth is a module which takes away the pain involved with anything authentication related: logins, signups, updating users, tokens, ... The main focus of simple auth is to allow you do all of the above in nothing more than one line of code (unless of course you include customizations for how these features work).
+
+You can install auth through composer or leaf cli.
+
+```bash
+composer require leafs/auth v1.1.2
+```
+
+or
+
+```bash
+leaf install auth@1.1.2
+```
+
+::: warning Note that
+Leaf auth has gone through a complete rewrite. Although it works the same way, features like sessions are used a bit differently. Read the changes section below for more info.
+:::
+
+## Usage
+
+The first thing to do is to connect to your database. Leaf Auth will need to search for users in your database to perform auth checks on.
+
+To connect to your database, you can add your db credentials in `connect` or use `autoConnect` if you've already configured your database in your `.env` file.
+
+```php
+use Leaf\Auth;
+
+Auth::connect("host", "user", "password", "dbname");
+// or
+Auth::autoConnect();
+```
+
+::: tip Functional Mode ⚡️
+Just as with other leaf modules, Leaf auth is able to extend leaf 3's functional mode to allow you easily and quickly handle authentication in your apps without having to use length namespaces and classes.
+
+Leaf auth provides the `auth` function which we will be using in the below code.
+
+**Getting started:**
+
+To get started with functional mode in leaf auth, you simply need to call the `auth` method.
+
+```php
+auth()->connect("127.0.0.1", "root", "", "dbName");
+```
+
+From there, you can do anything you want to with the auth method. Just as with the leaf core library itself, you no longer even need to initialize leaf auth.
+
+:::
+
+## Auth Config
+
+Auth Config was added in v2.4.0-beta to give you more control over how leaf handles authentication in your apps. Auth has been configured perfectly for most apps, but not all use cases are the same, hence, this brilliant addition.
+
+This also includes various configurations for doing things like:
+
+- Setting custom token lifetime
+- Hiding/Showing user fields
+- Adding/removing default timestamps
+- Changing the default password key
+- Setting custom password encode methods
+- Turning off password encoding totally
+- Setting custom password verify methods
+- Hiding/Showing password field
+- Adding custom validation messages
+- Turning off experimental feature warnings (NEW)
+- Configuring tokens
+
+### config
+
+To set a config variable, you can simply call the `config` method.
+
+```php
+auth()->config("item", "value");
+```
+
+You can also pass in an array to set multiple configs at once:
+
+```php
+auth()->config([
+ "item" => "value",
+ "item2" => "value"
+]);
+```
+
+### Settings
+
+Below is a list of all available settings.
+
+### USE_TIMESTAMPS
+
+This determines whether Leaf should add the default `created_at` and `updated_at` timestamps on register and update. Default is `true`.
+
+### PASSWORD_ENCODE
+
+*This setting has gone through a lot of changes since v2.4 beta, and may not work exactly the same way*. This setting is run when leaf wants to encode a password. It now uses `PASSWORD_DEFAULT` by defaullt for encryption.
+
+```php
+// This turns off password encoding
+auth()->config("PASSWORD_ENCODE", false);
+
+// defult encoding (Leaf\Helpers\Password::hash)
+auth()->config("PASSWORD_ENCODE", null);
+
+// use md5. We're still keeping support for md5 :-)
+auth()->config("PASSWORD_ENCODE", Password::MD5);
+
+// use custom method
+auth()->config("PASSWORD_ENCODE", function ($password) {
+ return Password::hash($password);
+});
+```
+
+### PASSWORD_VERIFY
+
+This setting is called when Leaf tries to verify a password. It works just like `PASSWORD_ENCODE` above.
+
+```php
+// This turns off password encoding
+auth()->config("PASSWORD_VERIFY", false);
+
+// defult encoding (Leaf\Helpers\Password::hash)
+auth()->config("PASSWORD_VERIFY", null);
+
+// use md5. We're still keeping support for md5 :-)
+auth()->config("PASSWORD_VERIFY", Password::MD5);
+
+// use custom method
+auth()->config("PASSWORD_VERIFY", function ($password) {
+ return Password::verify($password);
+});
+```
+
+### PASSWORD_KEY
+
+This allows you to change the password field name, maybe yours is passcode? This tells leaf to look for a user's password in that field. The example below tells leaf to search for passwords in the `passcode` column. (the default field is password)
+
+```php
+auth()->config("PASSWORD_KEY", "passcode");
+```
+
+### ID_KEY
+
+`ID_KEY` allows you to set your primary key name. For instance, you might have used `_id` instead of `id`. This setting allows you to quickly and effectively switch your key name.
+
+```php
+auth()->config("ID_KEY", "_id");
+```
+
+### USE_UUID
+
+This simply allows you to set the value for user ids on your own. This is done in order to add support for UUIDs in your registrations and not go with the default SQL increments.
+
+```php
+auth()->config("USE_UUID", UUID::v4());
+```
+
+### HIDE_ID
+
+This is a boolean which determines whether to hide the id in the user object returned on login/register. Default is `true`.
+
+### AUTH_NO_PASS
+
+This allows you to *manually* tell leaf auth that no password is required for authentication. When this is set to true, leaf auth will assume there is no password and act accordingly. If there is no password field set in the credentials passed into the `login` or `register` methods, leaf auth will automatically set this to `true`.
+
+### HIDE_PASSWORD
+
+Just as the name implies, allows you to hide or show the password in the final results returned from auth. Default is `true`.
+
+### LOGIN_PARAMS_ERROR
+
+This is the error to show if there's an error with any parameter which isn't the password eg: username:
+
+```php
+auth()->config("LOGIN_PARAMS_ERROR", "Username is incorrect!");
+```
+
+Default is `Incorrect credentials!`.
+
+### LOGIN_PASSWORD_ERROR
+
+This is the error to show if there's an error with the password.
+
+Default is `Password is incorrect!`.
+
+```php
+auth()->config("LOGIN_PASSWORD_ERROR", "Password is incorrect!");
+```
+
+### USE_SESSION
+
+Use session based authentication instead of the default JWT based auth. Without this setting enbled, you can't use any of the session methods below. Default is `false`.
+
+### SESSION_ON_REGISTER
+
+If true, a session will be created on a successful registration, else you it'll be created on login rather. Default is `false`.
+
+### GUARD_LOGIN
+
+The page route. Default is `/auth/login`.
+
+### GUARD_REGISTER
+
+The register page route. Default is `/auth/register`.
+
+### GUARD_LOGOUT
+
+Logout route handler. Default is `/auth/logout`.
+
+### GUARD_HOME
+
+Home page route. Default is `/home`.
+
+### SAVE_SESSION_JWT
+
+Add an auth token to the auth session? This allows you save a generated JWT to the session. You might want to use this if you want to extend your app into an API. Default is `false`.
+
+### EXPERIMENTAL_WARNINGS
+
+This option controls whether to show/hide experimental warnings from session components. Default is `true`. Turning this off allows you to use guards for JWT auth.
+
+### TOKEN_LIFETIME
+
+How long the token can be used before it expires. Default is 1 day.
+
+### TOKEN_SECRET
+
+This is the secret key used to generate tokens for users on signup and register.
+
+::: danger Leaf Auth Refactor 🔥
+The leaf auth module has been broken up into subclasses for easier use and performance reasons. If you only use login and signup, there's no need to include a class with tons of features that you may not use.
+
+This doesn't change the way leaf auth works as this was done for performance and maintainability reasons. You can still use the auth class just as done in Leaf 2, however, this has been optimized using static methods which means unnecessary code will not be run.
+
+```php
+Leaf\Auth::session();
+```
+
+:::
+
+## Session support
+
+::: tip Leaf Auth Session
+Session has been moved into a sub class for easier management. To use auth session methods, you now have to use the `Leaf\Auth\Session` class.
+
+```php
+Leaf\Auth\Session::init();
+```
+
+This doesn't affect the use of the auth class, since it works just as it did in earlier versions.
+:::
+
+Session based authentication as the name implies creates and manages a session during the authentication to manage the user's logged in state. And all of this is done in 1 or 2 lines of code to maintain the simplicity and flexibility Leaf auth has always given.
+
+To get started with session support, just set the `USE_SESSION` setting to true.
+
+```php
+auth()->config("USE_SESSION", true);
+```
+
+A much simpler way would be to simply call the `useSession` method.
+
+```php
+auth()->useSession();
+```
+
+## Session methods
+
+Enabling session support allows you to use some special methods and behaviours which are not available with the regular JWT authentication.
+
+### guard
+
+The guard method works sort of like authentication middleware. It takes in a single param, an array holding the authentication state or the type of guard to load up.
+
+```php
+Leaf\Auth\Session::guard(["hasAuth" => true]);
+
+// or
+
+Leaf\Auth\Session::guard("auth");
+
+// guest route redirects to home
+// route if you're logged in
+Leaf\Auth\Session::guard("guest");
+```
+
+This is a lot easier with functional mode
+
+```php
+auth()->guard("guest");
+```
+
+::: tip The auth function
+Besides returning the auth object, you can also directly run a guard on the auth method.
+
+```php
+auth("guest");
+```
+
+:::
+
+### save
+
+This method is used to save data to the auth session.
+
+```php
+Leaf\Auth\Session::save("rememberLogin", false);
+
+// You can add multiple vars
+Leaf\Auth\Session::save([
+ "rememberLogin" => false,
+ "sessionActivity" => "login"
+]);
+```
+
+As usual, this is easier with the auth class or with functional mode
+
+```php
+auth()->save("rememberLogin", false);
+```
+
+### length
+
+With length, you can get how long a user has been logged in. You can save the session time logs to your database in order to track users' login logs. The available logs are `SESSION_STARTED_AT` and `SESSION_LAST_ACTIVITY` which are automatically tracked by Leaf.
+
+```php
+// LoginsDB is a user defined method to save a login log
+
+// ...
+LoginsDB::params(
+ "logged_in_at",
+ date("D, d M Y H:i:s", Leaf\Auth\Session::length()),
+);
+
+LoginsDB::save();
+```
+
+Or with functional mode
+
+```php
+auth()->sessionLength();
+```
+
+### lastActive
+
+lastActive allows you to get how much time has passed since the last session activity.
+
+```php
+$userLastSeen = Leaf\Auth\Session::lastActive();
+```
+
+### refresh
+
+As the name implies, you can refresh the session with this method. Refreshing sort of restarts the session, but you can keep the user's old session data if you wish to.
+
+```php
+if ($newAccountAdded) {
+ // will delete old session data
+ Leaf\Auth\Session::refresh();
+} else {
+ // will keep session data
+ Leaf\Auth\Session::refresh(false);
+}
+```
+
+### status
+
+`status` checks whether a user session is ongoing by looking for keys specific to Leaf session auth so it doesn't confuse a Leaf auth session with user defined sessions. Returns true if a session is found and false if there's no session found.
+
+```php
+if (Leaf\Auth\Session::status()) {
+ return "logged in";
+} else {
+ return "guest mode";
+}
+```
+
+or with functional mode
+
+```php
+if (auth()->session()) {
+ return "logged in";
+} else {
+ return "guest mode";
+}
+```
+
+### end
+
+Of course we'll need a method to logout/end our session. This is just the method for that.
+
+```php
+Leaf\Auth\Session::end();
+```
+
+Or with functional mode
+
+```php
+auth()->endSession();
+```
+
+## Authentication methods
+
+### login
+
+Login is used to create a simple, secure user login. It takes in a table to search for users and a set of parameters for the login.
+
+```php
+$user = Leaf\Auth::login("users", [
+ "username" => "mychi.darko",
+ "password" => md5("test")
+]);
+```
+
+::: tip LOGIN CLASS
+Leaf auth now allows you to use logins with the new `Leaf\Auth\Login` class. This will allows you to import only the login functionality without actually going through the whole auth class.
+
+```php
+$user = Leaf\Auth\Login::user("users", [
+ "username" => "mychi.darko",
+ "password" => md5("test")
+]);
+```
+
+:::
+
+You can also use functional mode:
+
+```php
+$user = auth()->login("users", [
+ "username" => "mychi.darko",
+ "password" => md5("test")
+]);
+```
+
+If the user is successfully found, the user data is returned, if not, `null` is returned. You can get any error by calling the `errors` method.
+
+```php
+$user = auth()->login("users", [
+ "username" => "mychi.darko",
+ "password" => md5("test")
+]); // returns null if failed
+
+if (!$user) {
+ response()->throwErr(Leaf\Auth::errors());
+}
+```
+
+example success response:
+**Note that the password and id fields are removed**. You can control whether fields should be hidden from the returned value in the Auth settings.
+
+```php
+[
+ "user" => [
+ "username" => "mychi.darko",
+ "email" => "mychi@leafphp.dev",
+ "created_at" => "2019-09-20 13:47:48"
+ ],
+ "token" => "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NzYxMzUzMjgsImlzcyI6ImxvY2FsaG9zdCIsImV4cCI6MTU3NjEzNjIyOCwidXNlcklkIjoxfQ.7FODXGGJKioGQVX4ic0DJLoMIQTVUlsd4zFAJA4DAkg"
+]
+```
+
+#### session support
+
+Login received session support which allows login to create a session instead of returning aa JWT as done by default. To get started with session, just set the `USE_SESSION` setting or call the `init` method on auth session.
+
+```php
+Leaf\Auth\Session::init();
+
+Leaf\Auth::login("users", [
+ "username" => $username,
+ "password" => $password
+]);
+```
+
+Or with functional mode:
+
+```php
+auth()->useSession();
+
+auth()->login("users", [
+ "username" => $username,
+ "password" => $password
+]);
+```
+
+When the login succeeds, you'll be redirected to GUARD_HOME. You can configure the GUARD_HOME route to match the needs of your app.
+
+In case there's something wrong and Auth can't sign the user in, it returns a falsy value.
+
+```php
+$user = auth()->login("users", [
+ "username" => $username,
+ "password" => $password
+]);
+
+if (!$user) {
+ // you can pass the auth errors into a view
+ return $blade->render("pages.auth.login", [
+ "errors" => auth()->errors(),
+ "username" => $username,
+ "password" => $password,
+ ]);
+}
+```
+
+#### Password Encoding
+
+From v2.4-beta onwards, password encoding will no longer be available on the login method, you have to configure it among the Auth settings instead.
+
+`login` has a 3rd parameter which is an array of validation rules for login data. You can checkout the form module for all the validation rules.
+
+```php{1}
+$rules = ["username" => "ValidUsername"];
+
+$user = auth()->login("users", $loginData, $rules);
+```
+
+To get any errors, you need to call the `errors` method
+
+```php
+if (!$user) {
+ response()->throwErr(auth()->errors());
+}
+```
+
+
+
+### register
+
+Register is a simple method used to create simple, secure user registrations. This option was `basicRegister` in earlier versions. It takes in a table to save users, the params(array) to save.
+
+```php
+auth()->register("users", [
+ "username" => "mychi.darko",
+ "email" => "mychi@leafphp.dev",
+ "field" => "value"
+]);
+```
+
+::: tip REGISTER CLASS
+Leaf auth now allows you to register users with the new `Leaf\Auth\Register` class. This will allows you to import only the register functionality without actually going through the whole auth class.
+
+```php
+$user = auth()->register("users", [
+ "username" => "mychi.darko",
+ "email" => "mychi@leafphp.dev",
+ "field" => "value"
+]);
+```
+
+:::
+
+If the user is successfully saved, the user data is returned, if not, `false` is returned. You can get any error by calling the `errors` method.
+
+```php
+$user = auth()->register("users", [
+ "username" => "mychi.darko",
+ "email" => "mychi@leafphp.dev",
+ "field" => "value"
+]); // returns false if failed
+
+if ($user == false) {
+ response()->throwErr(auth()->errors());
+}
+```
+
+#### Uniques
+
+Let's say you want to check whether the username a user just entered has been taken, you'd have to write a bunch of conditional code, making the code count larger and more error prone, right?
+
+Well, `register` solves this problem smoothly. `register` has a 3rd parameter: an array of unique values which makes sure that the same value can't be saved twice.
+
+```php
+$db->register("users",
+ ["name" => "mychi", "email" => "m@m.com", "pass" => "1234"],
+ ["name", "email"]
+);
+```
+
+So, we're telling `register` to alert us if someone has already registered with the name `mychi` or the email `m@m.com`. This is because we passed `["name", "email"]` as the 3rd param to `register`
+
+**With uniques, you can cut down on your whole app:**
+For instance, if you know the exact data you'll be receiving in your app, let's say a username, email and password from a register form, you can do something like this:
+
+```php
+app()->post("/register", function () {
+ auth()->register(
+ "users",
+ request()->body(),
+ ["username", "email"]
+ );
+});
+```
+
+So, we pass in the entire request body, which contains the username, email and password. Simple right?
+
+For an even better way, you can make sure that only the data you need is going into the database. You can do this to retrieve only the fields you need.
+
+```php
+// select only the username, email and password from the request body
+$data = request()->get(["username", "email", "password"]);
+
+auth()->register("users", $data);
+```
+
+The password encode option here has also been removed. Use the auth config above instead. The final parameter is now the validate param which is an array of rules to test the params.
+
+```php
+app()->post("/register", function () use($app) {
+ auth()->register(
+ "users",
+ request()->body(),
+ ["username", "email"],
+ ["email" => "email"]
+ );
+});
+```
+
+#### register session support
+
+Just as with login, register now integrates with session. To turn this feature on, just set the `USE_SESSION` setting or call the `useSession` method.
+
+```php
+auth()->useSession();
+
+auth()->register("users", $credentials, [
+ "username", "email"
+]);
+```
+
+After a successful registration, you can redirect to GUARD_HOME or rather GUARD_LOGIN if you want the user to login after registration.
+
+```php
+// set your login route...default is /auth/login
+auth()->config("GUARD_LOGIN", "/login");
+
+// Redirect to login after auth
+auth()->config("SESSION_ON_REGISTER", false);
+
+// Login automatically after registration
+auth()->config("SESSION_ON_REGISTER", true);
+```
+
+In case there's something wrong and Auth can't register the user, it returns a falsy value.
+
+```php
+$user = auth()->register("users", $credentials, [
+ "username", "email"
+]);
+
+if (!$user) {
+ // you can pass the auth errors into a view
+ return $blade->render("pages.auth.register", [
+ "errors" => auth()->errors(),
+ "username" => $username,
+ "email" => $email,
+ "password" => $password,
+ ]);
+}
+```
+
+
+
+### update
+
+There's a login method, a register method, so why not a user update method? This method takes the stress out of updating a user's information. Update takes in 5 parameters:
+
+- The table to look for users
+- The data to update
+- Credentials to find user by
+- Unique values (optional)
+- Validation array (optional)
+
+```php
+// data to update
+$data = $request->get(["username", "email"]);
+
+// credentials to find user by
+$where = ["id" => 2];
+
+// unique data
+$uniques = ["username", "email"];
+
+// validation
+$validation = ["username" => "ValidUsername", "email" => "email"];
+
+$user = auth()->update("users", $data, $where, $uniques, $validation);
+```
+
+::: tip USER CLASS
+Leaf auth now allows you to register users with the new `Leaf\Auth\USER` class. This will allows you to import only the update functionality without actually going through the whole auth class.
+
+```php
+$user = Leaf\Auth\User::update("users", [
+ "username" => "mychi.darko",
+ "email" => "mychi@leafphp.dev",
+ "field" => "value"
+], ["id" => "1"]);
+```
+
+:::
+
+**Something little:** Uniques in `update` work a bit different from `register`, in `update`, Leaf tries to find another user which isn't the current user that has the same credentials. So if there's no other user with that same param value, the unique test passes. In short, **the current user is excluded from the users to check for same credentials**
+
+#### update session support
+
+Update also reeived session support. When a user is updated, the user is updated in the session and the updated user is also returned.
+
+```php
+$user = auth()->update("users", $data, $where, $uniques);
+```
+
+
+
+
+
+### user
+
+When tokens are added inside requests, you generally have to decode the token and query your database with the id returned to get the current user. Although Leaf Auth makes it really simple, it can get even simpler; by calling a single method. It takes in one parameter, the table to look for users.
+
+```php
+$user = auth()->user("users");
+return $user["name"];
+```
+
+In v2.4 beta, the table is set to `users` by default. So you can simply do this:
+
+```php
+$user = auth()->user();
+```
+
+We can catch any errors that occur, from fetching the user, working with the token...
+
+```php
+$user = auth()->user() ?? $request->throwErr(auth()->errors());
+```
+
+`user` also takes in a second parameter, which is an array of items to hide from the user array.
+
+```php
+$user = auth()->user("users", ["id", "password"]);
+```
+
+
+
+### id
+
+This is a method that decodes a token and returns the `user_id` field encoded in it.
+
+```php
+$user_id = auth()->id();
+```
+
+
+
+
+
+Leaf Auth now uses the `Leaf\Helpers\Authentication` package to provide solutions for token authentication. This provides a simple way to work with manual authentication and tokens. All methods here are now available in `Leaf\Auth`.
+
+```php
+$payload = auth()->validate($token);
+```
+
+
diff --git a/apps/docs/src/modules/auth/v/2.1/config.md b/apps/docs/src/modules/auth/v/2.1/config.md
new file mode 100755
index 0000000..1558863
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2.1/config.md
@@ -0,0 +1,206 @@
+# Auth Config
+
+Auth Config was added to give you more control over how leaf handles authentication in your apps. Auth has been configured perfectly for most apps, but not all use cases are the same, hence, this brilliant addition.
+
+This also includes various configurations for doing things like:
+
+- Setting custom token lifetime
+- Hiding/Showing user fields
+- Adding/removing default timestamps
+- Changing the default password key
+- Setting custom password encode methods
+- Turning off password encoding totally
+- Setting custom password verify methods
+- Hiding/Showing password field
+- Adding custom validation messages
+- Configuring tokens
+
+## config
+
+To set a config variable, you can simply call the `config` method.
+
+```php
+auth()->config("item", "value");
+```
+
+You can also pass in an array to set multiple configs at once:
+
+```php
+auth()->config([
+ "item" => "value",
+ "item2" => "value"
+]);
+```
+
+## Settings
+
+Below is a list of all available settings.
+
+### DB_TABLE
+
+
+The `DB_TABLE` config allows you to set a particular table which leaf auth will perform operations on. Leaf auth will use this database table for storing and retrieving users. By default, it is set to `users`. This allows you to login, signup, update and fetch users without explicitly adding a table each time.
+
+### USE_TIMESTAMPS
+
+This determines whether Leaf should add the default `created_at` and `updated_at` timestamps on register and update. Default is `true`.
+
+### PASSWORD_ENCODE
+
+*This setting has gone through a lot of changes since v2.4 beta, and may not work exactly the same way*. This setting is run when leaf wants to encode a password. It now uses `PASSWORD_DEFAULT` by defaullt for encryption.
+
+```php
+// This turns off password encoding
+auth()->config("PASSWORD_ENCODE", false);
+
+// defult encoding (Leaf\Helpers\Password::hash)
+auth()->config("PASSWORD_ENCODE", null);
+
+// use md5. We're still keeping support for md5 :-)
+auth()->config("PASSWORD_ENCODE", Password::MD5);
+
+// use custom method
+auth()->config("PASSWORD_ENCODE", function ($password) {
+ return Password::hash($password);
+});
+```
+
+### PASSWORD_VERIFY
+
+This setting is called when Leaf tries to verify a password. It works just like `PASSWORD_ENCODE` above.
+
+```php
+// This turns off password encoding
+auth()->config("PASSWORD_VERIFY", false);
+
+// defult encoding (Leaf\Helpers\Password::hash)
+auth()->config("PASSWORD_VERIFY", null);
+
+// use md5. We're still keeping support for md5 :-)
+auth()->config("PASSWORD_VERIFY", Password::MD5);
+
+// use custom method
+auth()->config("PASSWORD_VERIFY", function ($password) {
+ return Password::verify($password);
+});
+```
+
+### PASSWORD_KEY
+
+This allows you to change the password field name, maybe yours is passcode? This tells leaf to look for a user's password in that field. The example below tells leaf to search for passwords in the `passcode` column. (the default field is password)
+
+```php
+auth()->config("PASSWORD_KEY", "passcode");
+```
+
+### ID_KEY
+
+`ID_KEY` allows you to set your primary key name. For instance, you might have used `_id` instead of `id`. This setting allows you to quickly and effectively switch your key name.
+
+```php
+auth()->config("ID_KEY", "_id");
+```
+
+### USE_UUID
+
+This simply allows you to set the value for user ids on your own. This is done in order to add support for UUIDs in your registrations and not go with the default SQL increments.
+
+```php
+auth()->config("USE_UUID", UUID::v4());
+```
+
+### HIDE_ID
+
+This is a boolean which determines whether to hide the id in the user object returned on login/register. Default is `true`.
+
+### AUTH_NO_PASS
+
+This allows you to *manually* tell leaf auth that no password is required for authentication. When this is set to true, leaf auth will assume there is no password and act accordingly. If there is no password field set in the credentials passed into the `login` or `register` methods, leaf auth will automatically set this to `true`.
+
+### HIDE_PASSWORD
+
+Just as the name implies, allows you to hide or show the password in the final results returned from auth. Default is `true`.
+
+### LOGIN_PARAMS_ERROR
+
+This is the error to show if there's an error with any parameter which isn't the password eg: username:
+
+```php
+auth()->config("LOGIN_PARAMS_ERROR", "Username is incorrect!");
+```
+
+Default is `Incorrect credentials!`.
+
+### LOGIN_PASSWORD_ERROR
+
+This is the error to show if there's an error with the password.
+
+Default is `Password is incorrect!`.
+
+```php
+auth()->config("LOGIN_PASSWORD_ERROR", "Password is incorrect!");
+```
+
+### USE_SESSION
+
+Use session based authentication instead of the default JWT based auth. Without this setting enbled, you can't use any of the session methods below. Default is `false`.
+
+### SESSION_ON_REGISTER
+
+If true, a session will be created on a successful registration, else you it'll be created on login rather. Default is `false`.
+
+### GUARD_LOGIN
+
+The page route. Default is `/auth/login`.
+
+### GUARD_REGISTER
+
+The register page route. Default is `/auth/register`.
+
+### GUARD_LOGOUT
+
+Logout route handler. Default is `/auth/logout`.
+
+### GUARD_HOME
+
+Home page route. Default is `/home`.
+
+### SAVE_SESSION_JWT
+
+Add an auth token to the auth session? This allows you save a generated JWT to the session. You might want to use this if you want to extend your app into an API. Default is `false`.
+
+### TOKEN_LIFETIME
+
+How long the token can be used before it expires. Default is 1 day.
+
+### TOKEN_SECRET
+
+This is the secret key used to generate tokens for users on signup and register.
+
+::: danger Leaf Auth Refactor 🔥
+The leaf auth module has been broken up into subclasses for easier use and performance reasons. If you only use login and signup, there's no need to include a class with tons of features that you may not use.
+
+This doesn't change the way leaf auth works as this was done for performance and maintainability reasons. You can still use the auth class just as done in Leaf 2, however, this has been optimized using static methods which means unnecessary code will not be run.
+
+```php
+Leaf\Auth::session();
+```
+
+:::
+
+
diff --git a/apps/docs/src/modules/auth/v/2.1/helpers.md b/apps/docs/src/modules/auth/v/2.1/helpers.md
new file mode 100644
index 0000000..ccbcc31
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2.1/helpers.md
@@ -0,0 +1,137 @@
+# Helper Methods
+
+Leaf Auth already has methods that cater for most of your authentication use-cases, however, you may have a flow that is more complex than what Leaf Auth offers, for this reason, Leaf Auth provides low-level helper methods that allow you build auth flows from the ground up.
+
+## Working with JWTs
+
+According to the [JWT docs](https://jwt.io/introduction), JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
+
+In authentication, you can use JWTs as an identifier that a user has successfully logged in. The JWT usually also contains information about the authenticated user, usually the user's id. Leaf auth has helpers for interacting with JWTs. We'll take a look at them in this document.
+
+## Generating JWTs
+
+Leaf provides multiple ways for generating JWTs.
+
+- ### Using the `generateSimpleToken` method
+
+ The simplest method is to use the `generateSimpleToken` method on the `Leaf\Helpers\Authentication` class. This method takes in 3 parameters:
+
+ - The user id of the currently authenticated user
+ - The jwt secret string
+ - The jwt expiry time in seconds
+
+ ```php
+ use Leaf\Helpers\Authentication;
+
+ // ... your application code
+
+ Authentication::generateSimpleToken(
+ $userId,
+ 'MY_JWT_SECRET_STRING',
+ time() + 3600
+ );
+ ```
+
+- ### Using the `generateToken` method
+
+ The `generateToken` method on the `Leaf\Helpers\Authentication` class is a more advanced method for generating JWTs. It takes in 2 parameters:
+
+ - An array of claims to be added to the JWT
+ - The JWT secret string
+
+ ```php
+ use Leaf\Helpers\Authentication;
+
+ // ... your application code
+
+ Authentication::generateToken(
+ [
+ 'user_id' => $userId,
+ 'iat' => time(),
+ 'iss' => 'localhost',
+ 'exp' => time() + 3600,
+ ],
+ 'MY_JWT_SECRET_STRING'
+ );
+ ```
+
+## Getting JWTs from the request
+
+Tokens from authenticated requests are usually sent in the `Authorization` header. Leaf Auth provides helpers that allow you directly pull in tokens from the request header.
+
+### Using the `getAuthorizationHeader` method
+
+Leaf Auth provides the `getAuthorizationHeader` helper method for getting the token from the request header. It returns null if no authorization header is found.
+
+```php
+use Leaf\Helpers\Authentication;
+
+$header = Authentication::getAuthorizationHeader();
+```
+
+### Using the `getBearerToken` method
+
+Leaf Auth provides the `getBearerToken` helper method for getting the token from the request header. It returns null if no bearer token is found.
+
+```php
+use Leaf\Helpers\Authentication;
+
+$token = Authentication::getBearerToken();
+```
+
+## Verifying JWTs
+
+Verifying JWTs is a very important step in the authentication process. Tokens can be tampered with, they can also expire, or be issued by an untrusted source. For this reason, you should always verify the token before using it.
+
+Leaf Auth provides 2 methods for verifying JWTs.
+
+- ### Using the `validateToken` method
+
+This method automatically checks if there is a token in the request header, and if there is, it verifies the token. It returns the decoded token if the token is valid, and returns false if the token is invalid.
+
+It takes in a single parameter, which is the JWT secret string. This secret string should be the same as the one used to generate the token.
+
+```php
+use Leaf\Helpers\Authentication;
+
+$data = Authentication::validateToken($secret);
+```
+
+Note that this method will automatically return false if there is no token in the request header. You can use the `errors` method to get the error message which is why the token validation failed.
+
+```php
+use Leaf\Helpers\Authentication;
+
+$data = Authentication::validateToken($secret);
+
+if (!$data) {
+ $errors = Authentication::errors();
+}
+```
+
+- ### Using the `validate` method
+
+Unlike the `validateToken` method, this method does not automatically check for a token in the request header. It takes in 2 parameters:
+
+- The token to be validated
+- The JWT secret string
+
+```php
+use Leaf\Helpers\Authentication;
+
+$token = Authentication::getBearerToken();
+$data = Authentication::validate($token, $secret);
+```
+
+Just like the `validateToken` method, this method returns the decoded token if the token is valid, and returns false if the token is invalid. You can use the `errors` method to get the error message which is why the token validation failed.
+
+```php
+use Leaf\Helpers\Authentication;
+
+$token = Authentication::getBearerToken();
+$data = Authentication::validate($token, $secret);
+
+if (!$data) {
+ $errors = Authentication::errors();
+}
+```
diff --git a/apps/docs/src/modules/auth/v/2.1/index.md b/apps/docs/src/modules/auth/v/2.1/index.md
new file mode 100755
index 0000000..dbe826a
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2.1/index.md
@@ -0,0 +1,158 @@
+# Leaf Auth
+
+
+## Installation
+
+You can install Leaf Db with Leaf CLI:
+
+```bash
+leaf install auth
+```
+
+Or with composer:
+
+```bash
+composer require leafs/auth
+```
+
+From there, you can link your database and start writing some awesome queries.
+
+
+
+## Db Connection
+
+After installing leaf auth, you would need to connect to a database. Leaf auth will search for users and add/update users in this database when a login/register or update operation is called. There are a couple of ways to connect to a database with leaf auth.
+
+### connect
+
+The connect method allows you to pass in your database connection parameters directly to leaf auth.
+
+```php
+$auth = new Leaf\Auth;
+
+// syntax
+$auth->connect(
+ $host = '',
+ string $dbname = '',
+ string $user = '',
+ string $password = ''
+);
+
+// example
+$auth->connect('127.0.0.1', 'dbname', 'root', '');
+```
+
+### autoConnect
+
+This method allows you to connect to your database from parameters in a `.env` file. Most MVC frameworks and other libraries rely on a `.env` for a lot of configurations including the database. With `autoConnect`, you can directly pick up these configs.
+
+**example env:**
+
+```env
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=LeafMVC
+DB_USERNAME=root
+DB_PASSWORD=
+```
+
+**App:**
+
+```php
+$auth = new Leaf\Auth;
+$auth->autoConnect();
+```
+
+### PDO connection
+
+Leaf Auth also allows you to skip the entire connection process and share an existing PDO instance with leaf db. This allows you to gradually rewrite your existing apps with Leaf Auth without having multiple db connections and doing so at your own pace.
+
+```php
+$db = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
+
+auth()->dbConnection($db);
+
+// you can use leaf auth the same way you always have
+```
+
+Leaf Db has been rewritten based on PDO, this also means that you can pass your leaf db connection into leaf auth directly.
+
+```php
+$auth->dbConnection(db()->connection());
+```
+
+### Leaf db (auth v2 + leaf 3 only)
+
+If you are using leaf auth in a leaf 3 app, you will have access to the auth global as shown in some of the above connections. Along with this, if you already have a leaf db connection, you no longer need to explicitly connect to your database. Leaf auth searches for a leaf db instance and connects to it automatically.
+
+::: warning Note
+This only works in a leaf 3 app and only if you already have a leaf db connection.
+:::
+
+```php
+connect('127.0.0.1', 'dbname', 'username', 'password');
+
+// you can use auth straight away without any connect
+auth()->login(...);
+```
+
+## Functional Mode
+
+If you are using leaf auth v2 in a leaf 3 app, you will have access to the `auth` global which allows you to use Leaf Auth from anywhere in your entire application. You simply need to call `auth()` and leaf 3 will create and maintain a shared instance of Leaf auth which you can call from anywhere.
+
+This also means that you don't need to initialize leaf auth anymore.
+
+```php
+autoConnect();
+
+app()->get("/", function () {
+ // auth can be used here
+ // auth()->...
+});
+
+app()->run();
+```
+
+Functional mode also makes the `guard`, `hasAuth` and `sessionUser` globals available to you from anywhere.
+
+### guard
+
+The guard method is a shortcut method for `Auth::guard()`. You can find the guards documentation [here](/modules/auth/v/2.1/session.html#guard).
+
+### hasAuth
+
+`hasAuth` returns a boolean which is whether there's an active user session or not.
+
+### sessionUser
+
+This method returns the active session user or null if there's no session user.
+
+## Next Steps
+
+
diff --git a/apps/docs/src/modules/auth/v/2.1/methods.md b/apps/docs/src/modules/auth/v/2.1/methods.md
new file mode 100755
index 0000000..82c062c
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2.1/methods.md
@@ -0,0 +1,303 @@
+# Authentication methods
+
+These are the main functionality provided by leaf auth.
+
+::: tip New in v2.1
+Following the addition of the [DB_TABLE](/modules/auth/v/2.1/config.html#db-table) config, the table parameter has been removed from leaf auth. This means that you can now pass in only the credentials on a user `login`, `register` or `update`.
+:::
+
+## login
+
+Login is used to create a simple, secure user login.
+
+**It takes in a set of parameters for the login.**
+
+```php
+$user = auth()->login([
+ 'username' => 'mychi.darko',
+ 'password' => 'test'
+]);
+```
+
+If the user is successfully found, the user data is returned, if not, `null` is returned. You can get any error by calling the `errors` method.
+
+```php
+$user = auth()->login([
+ 'username' => 'mychi.darko',
+ 'password' => 'test'
+]); // returns null if failed
+
+if (!$user) {
+ response()->exit(auth()->errors());
+}
+```
+
+example success response:
+**Note that the password and id fields are removed**. You can control whether fields should be hidden from the returned value in the Auth settings.
+
+```php
+[
+ "user" => [
+ "username" => "mychi.darko",
+ "email" => "mychi@leafphp.dev",
+ "created_at" => "2019-09-20 13:47:48"
+ ],
+ "token" => "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NzYxMzUzMjgsImlzcyI6ImxvY2FsaG9zdCIsImV4cCI6MTU3NjEzNjIyOCwidXNlcklkIjoxfQ.7FODXGGJKioGQVX4ic0DJLoMIQTVUlsd4zFAJA4DAkg"
+]
+```
+
+### session support
+
+Login now has session support which allows login to create a session instead of returning a JWT as done by default. To get started with session, just set the `USE_SESSION` setting or call the `useSession` method.
+
+```php
+auth()->useSession();
+
+auth()->login([
+ 'username' => $username,
+ 'password' => $password
+]);
+```
+
+When the login succeeds, you'll be redirected to `GUARD_HOME`. You can configure the `GUARD_HOME` route to match the needs of your app.
+
+In case there's something wrong and Auth can't sign the user in, it returns a falsy value.
+
+```php
+auth()->useSession();
+
+$user = auth()->login([
+ 'username' => $username,
+ 'password' => $password
+]);
+
+if (!$user) {
+ // you can pass the auth errors into a view
+ return $blade->render('pages.auth.login', [
+ 'errors' => auth()->errors(),
+ 'username' => $username,
+ 'password' => $password,
+ ]);
+}
+```
+
+### Password Encoding
+
+Leaf auth has a very simple and straightforward implementation for password encoding. You can use default password protection with the leaf password helper or use your own method for hashing. All of this can be configured with [auth settings](/modules/auth/v/2.1/config.html#password-encode)
+
+### Validation
+
+This version of leaf auth has separated validation into it's own method. This allows you to have cleaner methods which are more readable. Validation uses [leaf form](/modules/forms/) under the hood, which makes it simple and easy to use. You can find more about form rules in the [leaf form validation docs](/modules/forms/#supported-rules).
+
+```php{1}
+$validation = auth()->validate(['firstname' => 'noSpaces']);
+
+if (!$validation) {
+ response()->exit(auth()->errors());
+}
+
+$user = auth()->login($loginData);
+
+if (!$user) {
+ response()->exit(auth()->errors());
+}
+```
+
+## register
+
+Register is a simple method used to create simple, secure user registrations. **It takes in the params(array) to save and any items which should be unique.**
+
+```php
+auth()->register([
+ 'username' => 'mychi.darko',
+ 'email' => 'mychi@leafphp.dev',
+ 'field' => 'value'
+]);
+```
+
+If the user is successfully saved, the user data is returned, if not, `null` is returned. You can get any error by calling the `errors` method.
+
+```php
+$user = auth()->register([
+ 'username' => 'mychi.darko',
+ 'email' => 'mychi@leafphp.dev',
+ 'field' => 'value'
+]); // returns null if failed
+
+if (!$user) {
+ response()->exit(auth()->errors());
+}
+```
+
+### Uniques
+
+Let's say you want to check whether the username a user just entered has been taken, you'd have to write a bunch of conditional code, making the code count larger and more error prone, right?
+
+Well, `register` solves this problem smoothly. `register` has a 2nd parameter: an array of unique values which makes sure that the same value can't be saved twice.
+
+```php
+auth()->register(
+ ['name' => 'mychi', 'email' => 'm@m.com', 'pass' => '1234'],
+ ['name', 'email']
+);
+```
+
+We are telling `register` to alert us if someone has already registered with the name `mychi` or the email `m@m.com`. This is because we passed `['name', 'email']` as the 2nd param to `register`.
+
+**With uniques, you can cut down on your whole app:**
+For instance, if you know the exact data you'll be receiving in your app, let's say a username, email and password from a register form, you can do something like this:
+
+```php
+app()->post('/register', function () {
+ auth()->register(request()->body(), ['username', 'email']);
+});
+```
+
+So, we pass in the entire request body, which contains the username, email and password. Simple right?
+
+For an even better way, you can make sure that only the data you need is going into the database. You can do this to retrieve only the fields you need.
+
+```php
+// select only the username, email and password from the request body
+$data = request()->get(['username', 'email', 'password']);
+
+auth()->register($data);
+```
+
+### `register` session support
+
+Just as with login, register now integrates with session. To turn this feature on, just set the `USE_SESSION` setting or call the `useSession` method.
+
+```php
+auth()->useSession();
+
+auth()->register($credentials, [
+ 'username', 'email'
+]);
+```
+
+After a successful registration, you can redirect to `GUARD_HOME` or rather `GUARD_LOGIN` if you want the user to login after registration.
+
+```php
+// set your login route...default is /auth/login
+auth()->config('GUARD_LOGIN', '/login');
+
+// Redirect to login after auth
+auth()->config('SESSION_ON_REGISTER', false);
+
+// Login automatically after registration
+auth()->config('SESSION_ON_REGISTER', true);
+```
+
+In case there's something wrong and Auth can't register the user, it returns a falsy value.
+
+```php
+$user = auth()->register($credentials, [
+ 'username', 'email'
+]);
+
+if (!$user) {
+ // you can pass the auth errors into a view
+ return $blade->render('pages.auth.register', [
+ 'errors' => auth()->errors(),
+ 'username' => $username,
+ 'email' => $email,
+ 'password' => $password,
+ ]);
+}
+```
+
+## update
+
+There's a login method, a register method, so why not a user update method? This method takes the stress out of updating a user's information. Update takes in 2 parameters:
+
+- The data to update
+- Unique values (optional)
+
+::: warning Changes in update
+The `update` method has been rewritten completely from the ground up. The biggest change is that you no longer need to pass in a condition for locating th user to update, but it also means that there needs to be a logged in user. `update` will now search for a JWT or user session to find the user to be updated.
+:::
+
+```php
+// data to update
+$data = request()->get(['username', 'email']);
+
+// unique data
+$uniques = ['username', 'email'];
+
+$user = auth()->update($data, $uniques);
+```
+
+::: tip Something little
+Uniques in `update` work a bit different from `register`, in `update`, Leaf tries to find another user which isn't the current user that has the same credentials. So if there's no other user with that same param value, the unique test passes. In short, **the current user is excluded from the users to check for same credentials**
+:::
+
+### `update` session support
+
+When a user is updated, the user is updated in the session and the updated user is also returned.
+
+```php
+$user = auth()->update($data, $uniques);
+```
+
+
+
+## user
+
+This is a method which allows you to get the user who is currently logged in. This method expects either a JWT or a session to exist on the request. `user` finds the user id and queries the user from the database linked to leaf auth. In the case of JWTs, it also validates the JWT and makes sure that it is valid and hasn't expired.
+
+```php
+$user = auth()->user();
+return $user['name'];
+```
+
+As mentioned, `user` queries your database for the full user information. You can specify your custom table using the [DB_TABLE](/modules/auth/v/2.1/config.html#db-table) config like this:
+
+```php
+$user = auth()->config('DB_TABLE', 'all_users');
+```
+
+We can catch any errors that occur, from fetching the user, working with the token...
+
+```php
+$user = auth()->user() ?? $request->exit(auth()->errors());
+```
+
+`user` also allows you to pass an array of items to hide from the returned user array.
+
+```php
+$user = auth()->user(['id', 'password']);
+```
+
+## id
+
+This method returns the id of the currently logged in user. In the case of JWTs, it decodes and validates the token and returns the `user_id` field encoded in it.
+
+```php
+$userId = auth()->id();
+```
+
+
+
+
+
+
diff --git a/apps/docs/src/modules/auth/v/2.1/session.md b/apps/docs/src/modules/auth/v/2.1/session.md
new file mode 100644
index 0000000..4f28999
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2.1/session.md
@@ -0,0 +1,120 @@
+# Session support
+
+Session based authentication as the name implies, creates and manages a session during the authentication process to track the user's logged in state. Leaf auth provides an easy and developer friendly approach to handle this.
+
+To get started with session support, just set the `USE_SESSION` config to true.
+
+```php
+auth()->config('USE_SESSION', true);
+```
+
+A much simpler way would be to simply call the `useSession` method.
+
+```php
+auth()->useSession();
+```
+
+## Session methods
+
+Enabling session support allows you to use some special methods and behaviours which are not available with the regular JWT authentication.
+
+### guard
+
+The guard method works sort of like authentication middleware. It takes in a single param, an array holding the authentication state or the type of guard to load up.
+
+```php
+auth()->guard('auth');
+
+// or
+
+Leaf\Auth::guard('auth');
+
+// guest route redirects to home
+// route if you're logged in
+Leaf\Auth::guard('guest');
+```
+
+::: tip The guard method
+You can directly run a guard on the `guard` method.
+
+```php
+guard('guest');
+```
+
+:::
+
+
+### length
+
+With length, you can get how long a user has been logged in. You can save the session time logs to your database in order to track users' login logs. The available logs are `SESSION_STARTED_AT` and `SESSION_LAST_ACTIVITY` which are automatically tracked by Leaf.
+
+```php
+$sessionDuration = auth()->length();
+```
+
+### lastActive
+
+`lastActive` allows you to get how much time has passed since the last session activity.
+
+```php
+$userLastSeen = auth()->lastActive();
+```
+
+### refresh
+
+As the name implies, you can refresh the session with this method. Refreshing sort of restarts the session, but you can keep the user's old session data if you wish to.
+
+```php
+if ($newAccountAdded) {
+ // will delete old session data
+ Leaf\Auth::refresh();
+} else {
+ // will keep session data
+ auth()->refresh(false);s
+}
+```
+
+### status
+
+`status` checks whether a user session is ongoing by looking for keys specific to Leaf session auth so it doesn't confuse a Leaf auth session with user defined sessions. Returns the user if a session is found and false if there's no session found.
+
+```php
+if (auth()->status()) {
+ return 'logged in';
+} else {
+ return 'guest mode';
+}
+```
+
+### logout
+
+Of course we'll need a method to logout/end our session. This is just the method for that.
+
+```php
+auth()->logout();
+```
+
+You can also pass in a route to redirect to after logging out.
+
+```php
+auth()->logout('/home');
+```
diff --git a/apps/docs/src/modules/auth/v/2.1/v1-new.md b/apps/docs/src/modules/auth/v/2.1/v1-new.md
new file mode 100644
index 0000000..7449df4
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2.1/v1-new.md
@@ -0,0 +1,3 @@
+# New in Leaf Auth
+
+New in leaf Auth
diff --git a/apps/docs/src/modules/auth/v/2.1/v2.0-new.md b/apps/docs/src/modules/auth/v/2.1/v2.0-new.md
new file mode 100644
index 0000000..7449df4
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2.1/v2.0-new.md
@@ -0,0 +1,3 @@
+# New in Leaf Auth
+
+New in leaf Auth
diff --git a/apps/docs/src/modules/auth/v/2/config.md b/apps/docs/src/modules/auth/v/2/config.md
new file mode 100755
index 0000000..6400c5f
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2/config.md
@@ -0,0 +1,218 @@
+---
+aside: none
+---
+
+# Auth Config
+
+Auth Config was added to give you more control over how leaf handles authentication in your apps. Auth has been configured perfectly for most apps, but not all use cases are the same, hence, this brilliant addition.
+
+This also includes various configurations for doing things like:
+
+- Setting custom token lifetime
+- Hiding/Showing user fields
+- Adding/removing default timestamps
+- Changing the default password key
+- Setting custom password encode methods
+- Turning off password encoding totally
+- Setting custom password verify methods
+- Hiding/Showing password field
+- Adding custom validation messages
+- Configuring tokens
+
+## config
+
+To set a config variable, you can simply call the `config` method.
+
+```php
+auth()->config("item", "value");
+```
+
+You can also pass in an array to set multiple configs at once:
+
+```php
+auth()->config([
+ "item" => "value",
+ "item2" => "value"
+]);
+```
+
+## Settings
+
+Below is a list of all available settings.
+
+### USE_TIMESTAMPS
+
+This determines whether Leaf should add the default `created_at` and `updated_at` timestamps on register and update. Default is `true`.
+
+### PASSWORD_ENCODE
+
+*This setting has gone through a lot of changes since v2.4 beta, and may not work exactly the same way*. This setting is run when leaf wants to encode a password. It now uses `PASSWORD_DEFAULT` by defaullt for encryption.
+
+```php
+// This turns off password encoding
+auth()->config("PASSWORD_ENCODE", false);
+
+// defult encoding (Leaf\Helpers\Password::hash)
+auth()->config("PASSWORD_ENCODE", null);
+
+// use md5. We're still keeping support for md5 :-)
+auth()->config("PASSWORD_ENCODE", Password::MD5);
+
+// use custom method
+auth()->config("PASSWORD_ENCODE", function ($password) {
+ return Password::hash($password);
+});
+```
+
+### PASSWORD_VERIFY
+
+This setting is called when Leaf tries to verify a password. It works just like `PASSWORD_ENCODE` above.
+
+```php
+// This turns off password encoding
+auth()->config("PASSWORD_VERIFY", false);
+
+// defult encoding (Leaf\Helpers\Password::hash)
+auth()->config("PASSWORD_VERIFY", null);
+
+// use md5. We're still keeping support for md5 :-)
+auth()->config("PASSWORD_VERIFY", Password::MD5);
+
+// use custom method
+auth()->config("PASSWORD_VERIFY", function ($password) {
+ return Password::verify($password);
+});
+```
+
+### PASSWORD_KEY
+
+This allows you to change the password field name, maybe yours is passcode? This tells leaf to look for a user's password in that field. The example below tells leaf to search for passwords in the `passcode` column. (the default field is password)
+
+```php
+auth()->config("PASSWORD_KEY", "passcode");
+```
+
+### ID_KEY
+
+`ID_KEY` allows you to set your primary key name. For instance, you might have used `_id` instead of `id`. This setting allows you to quickly and effectively switch your key name.
+
+```php
+auth()->config("ID_KEY", "_id");
+```
+
+### USE_UUID
+
+This simply allows you to set the value for user ids on your own. This is done in order to add support for UUIDs in your registrations and not go with the default SQL increments.
+
+```php
+auth()->config("USE_UUID", UUID::v4());
+```
+
+### HIDE_ID
+
+This is a boolean which determines whether to hide the id in the user object returned on login/register. Default is `true`.
+
+### AUTH_NO_PASS
+
+This allows you to *manually* tell leaf auth that no password is required for authentication. When this is set to true, leaf auth will assume there is no password and act accordingly. If there is no password field set in the credentials passed into the `login` or `register` methods, leaf auth will automatically set this to `true`.
+
+### HIDE_PASSWORD
+
+Just as the name implies, allows you to hide or show the password in the final results returned from auth. Default is `true`.
+
+### LOGIN_PARAMS_ERROR
+
+This is the error to show if there's an error with any parameter which isn't the password eg: username:
+
+```php
+auth()->config("LOGIN_PARAMS_ERROR", "Username is incorrect!");
+```
+
+Default is `Incorrect credentials!`.
+
+### LOGIN_PASSWORD_ERROR
+
+This is the error to show if there's an error with the password.
+
+Default is `Password is incorrect!`.
+
+```php
+auth()->config("LOGIN_PASSWORD_ERROR", "Password is incorrect!");
+```
+
+### USE_SESSION
+
+Use session based authentication instead of the default JWT based auth. Without this setting enbled, you can't use any of the session methods below. Default is `false`.
+
+### SESSION_ON_REGISTER
+
+If true, a session will be created on a successful registration, else you it'll be created on login rather. Default is `false`.
+
+### GUARD_LOGIN
+
+The page route. Default is `/auth/login`.
+
+### GUARD_REGISTER
+
+The register page route. Default is `/auth/register`.
+
+### GUARD_LOGOUT
+
+Logout route handler. Default is `/auth/logout`.
+
+### GUARD_HOME
+
+Home page route. Default is `/home`.
+
+### SAVE_SESSION_JWT
+
+Add an auth token to the auth session? This allows you save a generated JWT to the session. You might want to use this if you want to extend your app into an API. Default is `false`.
+
+### TOKEN_LIFETIME
+
+How long the token can be used before it expires. Default is 1 day.
+
+### TOKEN_SECRET
+
+This is the secret key used to generate tokens for users on signup and register.
+
+::: danger Leaf Auth Refactor 🔥
+The leaf auth module has been broken up into subclasses for easier use and performance reasons. If you only use login and signup, there's no need to include a class with tons of features that you may not use.
+
+This doesn't change the way leaf auth works as this was done for performance and maintainability reasons. You can still use the auth class just as done in Leaf 2, however, this has been optimized using static methods which means unnecessary code will not be run.
+
+```php
+Leaf\Auth::session();
+```
+
+:::
+
+
+
+## Next Steps
+
+
diff --git a/apps/docs/src/modules/auth/v/2/index.md b/apps/docs/src/modules/auth/v/2/index.md
new file mode 100755
index 0000000..9ce3521
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2/index.md
@@ -0,0 +1,154 @@
+# Auth v2.0
+
+
+## Installation
+
+You can install Leaf Db with Leaf CLI:
+
+```bash
+leaf install auth@2.0
+```
+
+Or with composer:
+
+```bash
+composer require leafs/auth v2.0
+```
+
+From there, you can link your database and start writing some awesome queries.
+
+
+
+## Db Connection
+
+After installing leaf auth, you would need to connect to a database. Leaf auth will search for users and add/update users in this database when a login/register or update operation is called. There are a couple of ways to connect to a database with leaf auth.
+
+### connect
+
+The connect method allows you to pass in your database connection parameters directly to leaf auth.
+
+```php
+$auth = new Leaf\Auth;
+
+// syntax
+$auth->connect(
+ $host = '',
+ string $dbname = '',
+ string $user = '',
+ string $password = ''
+);
+
+// example
+$auth->connect('127.0.0.1', 'dbname', 'root', '');
+```
+
+### autoConnect
+
+This method allows you to connect to your database from parameters in a `.env` file. Most MVC frameworks and other libraries rely on a `.env` for a lot of configurations including the database. With `autoConnect`, you can directly pick up these configs.
+
+**example env:**
+
+```env
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=LeafMVC
+DB_USERNAME=root
+DB_PASSWORD=
+```
+
+**App:**
+
+```php
+$auth = new Leaf\Auth;
+$auth->autoConnect();
+```
+
+### PDO connection
+
+Leaf Auth also allows you to skip the entire connection process and share an existing PDO instance with leaf db. This allows you to gradually rewrite your existing apps with Leaf Auth without having multiple db connections and doing so at your own pace.
+
+```php
+$db = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
+
+auth()->dbConnection($db);
+
+// you can use leaf auth the same way you always have
+```
+
+Leaf Db has been rewritten based on PDO, this also means that you can pass your leaf db connection into leaf auth directly.
+
+```php
+$auth->dbConnection(db()->connection());
+```
+
+### Leaf db (auth v2 + leaf 3 only)
+
+If you are using leaf auth in a leaf 3 app, you will have access to the auth global as shown in some of the above connections. Along with this, if you already have a leaf db connection, you no longer need to explicitly connect to your database. Leaf auth searches for a leaf db instance and connects to it automatically.
+
+::: warning Note
+This only works in a leaf 3 app and only if you already have a leaf db connection.
+:::
+
+```php
+connect('127.0.0.1', 'dbname', 'username', 'password');
+
+// you can use auth straight away without any connect
+auth()->login(...);
+```
+
+## Functional Mode
+
+If you are using leaf auth v2 in a leaf 3 app, you will have access to the `auth` global which allows you to use Leaf Auth from anywhere in your entire application. You simply need to call `auth()` and leaf 3 will create and maintain a shared instance of Leaf auth which you can call from anywhere.
+
+This also means that you don't need to initialize leaf auth anymore.
+
+```php
+autoConnect();
+
+app()->get("/", function () {
+ // auth can be used here
+ // auth()->...
+});
+
+app()->run();
+```
+
+Functional mode also makes the `guard`, `hasAuth` and `sessionUser` globals available to you from anywhere.
+
+### guard
+
+The guard method is a shortcut method for `Auth::guard()`. You can find the guards documentation [here](/modules/auth/v/2/session.html#guard).
+
+### hasAuth
+
+`hasAuth` returns a boolean which is whether there's an active user session or not.
+
+### sessionUser
+
+This method returns the active session user or null if there's no session user.
+
+## Next Steps
+
+
diff --git a/apps/docs/src/modules/auth/v/2/methods.md b/apps/docs/src/modules/auth/v/2/methods.md
new file mode 100755
index 0000000..21b2ecc
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2/methods.md
@@ -0,0 +1,309 @@
+---
+aside: none
+---
+
+# Authentication methods
+
+These are the main functionality provided by leaf auth.
+
+## login
+
+Login is used to create a simple, secure user login.
+
+**It takes in a table to search for users and a set of parameters for the login.**
+
+```php
+$user = auth()->login('users', [
+ 'username' => 'mychi.darko',
+ 'password' => 'test'
+]);
+```
+
+If the user is successfully found, the user data is returned, if not, `null` is returned. You can get any error by calling the `errors` method.
+
+```php
+$user = auth()->login('users', [
+ 'username' => 'mychi.darko',
+ 'password' => 'test'
+]); // returns null if failed
+
+if (!$user) {
+ response()->exit(auth()->errors());
+}
+```
+
+example success response:
+**Note that the password and id fields are removed**. You can control whether fields should be hidden from the returned value in the Auth settings.
+
+```php
+[
+ "user" => [
+ "username" => "mychi.darko",
+ "email" => "mychi@leafphp.dev",
+ "created_at" => "2019-09-20 13:47:48"
+ ],
+ "token" => "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NzYxMzUzMjgsImlzcyI6ImxvY2FsaG9zdCIsImV4cCI6MTU3NjEzNjIyOCwidXNlcklkIjoxfQ.7FODXGGJKioGQVX4ic0DJLoMIQTVUlsd4zFAJA4DAkg"
+]
+```
+
+### session support
+
+Login now has session support which allows login to create a session instead of returning a JWT as done by default. To get started with session, just set the `USE_SESSION` setting or call the `useSession` method.
+
+```php
+auth()->useSession();
+
+auth()->login('users', [
+ 'username' => $username,
+ 'password' => $password
+]);
+```
+
+When the login succeeds, you'll be redirected to `GUARD_HOME`. You can configure the `GUARD_HOME` route to match the needs of your app.
+
+In case there's something wrong and Auth can't sign the user in, it returns a falsy value.
+
+```php
+auth()->useSession();
+
+$user = auth()->login('users', [
+ 'username' => $username,
+ 'password' => $password
+]);
+
+if (!$user) {
+ // you can pass the auth errors into a view
+ return $blade->render('pages.auth.login', [
+ 'errors' => auth()->errors(),
+ 'username' => $username,
+ 'password' => $password,
+ ]);
+}
+```
+
+### Password Encoding
+
+Leaf auth has a very simple and straightforward implementation for password encoding. You can use default password protection with the leaf password helper or use your own method for hashing. All of this can be configured with [auth settings](/modules/auth/v/2/config.html#password-encode)
+
+### Validation
+
+This version of leaf auth has separated validation into it's own method. This allows you to have cleaner methods which are more readable. Validation uses [leaf form](/modules/forms/) under the hood, which makes it simple and easy to use. You can find more about form rules in the [leaf form validation docs](/modules/forms/#supported-rules).
+
+```php{1}
+$validation = auth()->validate(['username' => 'ValidUsername']);
+
+if (!$validation) {
+ response()->exit(auth()->errors());
+}
+
+$user = auth()->login('users', $loginData);
+
+if (!$user) {
+ response()->exit(auth()->errors());
+}
+```
+
+## register
+
+Register is a simple method used to create simple, secure user registrations. **It takes in a table to save users, the params(array) to save and any items which should be unique.**
+
+```php
+auth()->register('users', [
+ 'username' => 'mychi.darko',
+ 'email' => 'mychi@leafphp.dev',
+ 'field' => 'value'
+]);
+```
+
+If the user is successfully saved, the user data is returned, if not, `null` is returned. You can get any error by calling the `errors` method.
+
+```php
+$user = auth()->register('users', [
+ 'username' => 'mychi.darko',
+ 'email' => 'mychi@leafphp.dev',
+ 'field' => 'value'
+]); // returns null if failed
+
+if (!$user) {
+ response()->exit(auth()->errors());
+}
+```
+
+### Uniques
+
+Let's say you want to check whether the username a user just entered has been taken, you'd have to write a bunch of conditional code, making the code count larger and more error prone, right?
+
+Well, `register` solves this problem smoothly. `register` has a 3rd parameter: an array of unique values which makes sure that the same value can't be saved twice.
+
+```php
+auth()->register(
+ 'users',
+ ['name' => 'mychi', 'email' => 'm@m.com', 'pass' => '1234'],
+ ['name', 'email']
+);
+```
+
+We are telling `register` to alert us if someone has already registered with the name `mychi` or the email `m@m.com`. This is because we passed `['name', 'email']` as the 3rd param to `register`.
+
+**With uniques, you can cut down on your whole app:**
+For instance, if you know the exact data you'll be receiving in your app, let's say a username, email and password from a register form, you can do something like this:
+
+```php
+app()->post('/register', function () {
+ auth()->register(
+ 'users',
+ request()->body(),
+ ['username', 'email']
+ );
+});
+```
+
+So, we pass in the entire request body, which contains the username, email and password. Simple right?
+
+For an even better way, you can make sure that only the data you need is going into the database. You can do this to retrieve only the fields you need.
+
+```php
+// select only the username, email and password from the request body
+$data = request()->get(['username', 'email', 'password']);
+
+auth()->register('users', $data);
+```
+
+### `register` session support
+
+Just as with login, register now integrates with session. To turn this feature on, just set the `USE_SESSION` setting or call the `useSession` method.
+
+```php
+auth()->useSession();
+
+auth()->register('users', $credentials, [
+ 'username', 'email'
+]);
+```
+
+After a successful registration, you can redirect to GUARD_HOME or rather GUARD_LOGIN if you want the user to login after registration.
+
+```php
+// set your login route...default is /auth/login
+auth()->config('GUARD_LOGIN', '/login');
+
+// Redirect to login after auth
+auth()->config('SESSION_ON_REGISTER', false);
+
+// Login automatically after registration
+auth()->config('SESSION_ON_REGISTER', true);
+```
+
+In case there's something wrong and Auth can't register the user, it returns a falsy value.
+
+```php
+$user = auth()->register('users', $credentials, [
+ 'username', 'email'
+]);
+
+if (!$user) {
+ // you can pass the auth errors into a view
+ return $blade->render('pages.auth.register', [
+ 'errors' => auth()->errors(),
+ 'username' => $username,
+ 'email' => $email,
+ 'password' => $password,
+ ]);
+}
+```
+
+## update
+
+There's a login method, a register method, so why not a user update method? This method takes the stress out of updating a user's information. Update takes in 3 parameters:
+
+- The table to look for users
+- The data to update
+- Unique values (optional)
+
+::: warning Changes in update
+The `update` method has been rewritten completely from the ground up. The biggest change is that you no longer need to pass in a condition for locating th user to update, but it also means that there needs to be a logged in user. `update` will now search for a JWT or user session to find the user to be updated.
+:::
+
+```php
+// data to update
+$data = request()->get(["username", "email"]);
+
+// unique data
+$uniques = ["username", "email"];
+
+$user = auth()->update("users", $data, $uniques);
+```
+
+::: tip Something little
+Uniques in `update` work a bit different from `register`, in `update`, Leaf tries to find another user which isn't the current user that has the same credentials. So if there's no other user with that same param value, the unique test passes. In short, **the current user is excluded from the users to check for same credentials**
+:::
+
+### `update` session support
+
+When a user is updated, the user is updated in the session and the updated user is also returned.
+
+```php
+$user = auth()->update("users", $data, $uniques);
+```
+
+
+
+## user
+
+This is a method which allows you to get the user who is currently logged in. This method expects either a JWT or a session to exist on the request. `user` finds the user id and queries the user from the database linked to leaf auth. In the case of JWTs, it also validates the JWT and makes sure that it is valid and hasn't expired.
+
+```php
+$user = auth()->user();
+return $user['name'];
+```
+
+As mentioned, `user` queries your database for the full user information. By default, the table to look for users has been set to `users`. You can pass in a table of your choice like this:
+
+```php
+$user = auth()->user('all_users');
+```
+
+We can catch any errors that occur, from fetching the user, working with the token...
+
+```php
+$user = auth()->user() ?? $request->exit(auth()->errors());
+```
+
+`user` also takes in a second parameter, which is an array of items to hide from the returned user array.
+
+```php
+$user = auth()->user('users', ['id', 'password']);
+```
+
+## id
+
+This method returns the id of the currently logged in user. In the case of JWTs, it decodes and validates the token and returns the `user_id` field encoded in it.
+
+```php
+$userId = auth()->id();
+```
+
+
+
+
+
+## Next Steps
+
+
diff --git a/apps/docs/src/modules/auth/v/2/new.md b/apps/docs/src/modules/auth/v/2/new.md
new file mode 100644
index 0000000..7449df4
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2/new.md
@@ -0,0 +1,3 @@
+# New in Leaf Auth
+
+New in leaf Auth
diff --git a/apps/docs/src/modules/auth/v/2/session.md b/apps/docs/src/modules/auth/v/2/session.md
new file mode 100644
index 0000000..71cc432
--- /dev/null
+++ b/apps/docs/src/modules/auth/v/2/session.md
@@ -0,0 +1,137 @@
+---
+aside: none
+---
+
+# Session support
+
+Session based authentication as the name implies, creates and manages a session during the authentication process to track the user's logged in state. Leaf auth provides an easy and developer friendly approach to handle this.
+
+To get started with session support, just set the `USE_SESSION` config to true.
+
+```php
+auth()->config('USE_SESSION', true);
+```
+
+A much simpler way would be to simply call the `useSession` method.
+
+```php
+auth()->useSession();
+```
+
+## Session methods
+
+Enabling session support allows you to use some special methods and behaviours which are not available with the regular JWT authentication.
+
+### guard
+
+The guard method works sort of like authentication middleware. It takes in a single param, an array holding the authentication state or the type of guard to load up.
+
+```php
+auth()->guard('auth');
+
+// or
+
+Leaf\Auth::guard('auth');
+
+// guest route redirects to home
+// route if you're logged in
+Leaf\Auth::guard('guest');
+```
+
+::: tip The guard method
+You can directly run a guard on the `guard` method.
+
+```php
+guard('guest');
+```
+
+:::
+
+
+### length
+
+With length, you can get how long a user has been logged in. You can save the session time logs to your database in order to track users' login logs. The available logs are `SESSION_STARTED_AT` and `SESSION_LAST_ACTIVITY` which are automatically tracked by Leaf.
+
+```php
+$sessionDuration = auth()->length();
+```
+
+### lastActive
+
+`lastActive` allows you to get how much time has passed since the last session activity.
+
+```php
+$userLastSeen = auth()->lastActive();
+```
+
+### refresh
+
+As the name implies, you can refresh the session with this method. Refreshing sort of restarts the session, but you can keep the user's old session data if you wish to.
+
+```php
+if ($newAccountAdded) {
+ // will delete old session data
+ Leaf\Auth::refresh();
+} else {
+ // will keep session data
+ auth()->refresh(false);s
+}
+```
+
+### status
+
+`status` checks whether a user session is ongoing by looking for keys specific to Leaf session auth so it doesn't confuse a Leaf auth session with user defined sessions. Returns the user if a session is found and false if there's no session found.
+
+```php
+if (auth()->status()) {
+ return 'logged in';
+} else {
+ return 'guest mode';
+}
+```
+
+### logout
+
+Of course we'll need a method to logout/end our session. This is just the method for that.
+
+```php
+auth()->logout();
+```
+
+You can also pass in a route to redirect to after logging out.
+
+```php
+auth()->logout('/home');
+```
+
+## Next Steps
+
+
diff --git a/apps/docs/src/modules/cookies/index.md b/apps/docs/src/modules/cookies/index.md
new file mode 100755
index 0000000..59c11bc
--- /dev/null
+++ b/apps/docs/src/modules/cookies/index.md
@@ -0,0 +1,320 @@
+# Leaf Cookie
+
+
+
+Cookies are small pieces of text sent to a client's browser by your application. They help your app remember information about users' visits, which can both make it easier to visit your app and make it more useful to your users.
+
+The cookie module helps you create, interact with and manage your cookies.
+
+## Installation
+
+You can quickly install leaf cookies with composer or leaf cli.
+
+```bash
+leaf install cookie
+```
+
+or with composer:
+
+```bash
+composer require leafs/cookie
+```
+
+## Usage
+
+
+
+Right after installing the cookie module, you can start using it on the `cookie()` method like this:
+
+```php
+cookie()->set('name', 'Fullname');
+```
+
+
+
+
+Leaf cookie provides a `Leaf\Http\Cookie` class for quickly using cookie methods:
+
+```php
+use Leaf\Http\Cookie;
+
+...
+
+Cookie::set('name', 'Fullname');
+```
+
+
+
+## Setting Cookies
+
+The cookie module provides 3 methods for setting cookies:
+
+- `set()`
+- `simpleCookie()`
+- `response()->withCookie`
+
+### Set
+
+This method allows you to set a cookie which should be returned with your next response to the client. It takes in 3 params:
+
+- cookie name (string|array)
+- cookie value (optional - string)
+- cookie options (optional - array)
+
+
+
+```php
+// normal method
+cookie()->set('name', 'Fullname');
+
+// using array
+cookie()->set(['name' => 'Fullname']);
+```
+
+
+
+
+```php
+// normal method
+Cookie::set('name', 'Fullname');
+
+// using array
+Cookie::set(['name' => 'Fullname']);
+```
+
+
+
+You can also set multiple cookies at a time
+
+
+
+Cookies can also be set with options. These options allow you to set the cookie's expiry time, path, domain, secure and httponly. They determine how long the cookie should last and who should have access to it.
+
+
+
+### response()->withCookie
+
+This method allows you to set a cookie directly on the response object. It takes in 3 params:
+
+- cookie name (string)
+- cookie value (string)
+- cookie expiresAt (optional - string - default of 7 days)
+
+
+
+## Getting Cookies
+
+Just as you can set cookies, you can also get them from the client. The cookie module provides 2 methods for retrieve cookies:
+
+- `get()`
+- `all()`
+
+### get
+
+`get()` returns a particular set cookie
+
+
+
+### all
+
+`all()` returns all set cookies.
+
+
+
+```php
+$cookies = cookie()->all();
+```
+
+
+
+
+```php
+$cookies = Cookie::all();
+```
+
+
+
+## Deleting Cookies
+
+The cookie module provides 3 methods for deleting cookies:
+
+- `response()->withoutCookie()`
+- `unset()`
+- `unsetAll()`
+
+### response()->withoutCookie
+
+This method allows you to delete a cookie directly on the response object. It takes in 1 param which is the cookie to delete.
+
+
+
+### unsetAll
+
+This method removes all set cookies.
+
+
+
+```php
+cookie()->unsetAll();
+```
+
+
+
+
+```php
+Cookie::unsetAll();
+```
+
+
diff --git a/apps/docs/src/modules/cors/index.md b/apps/docs/src/modules/cors/index.md
new file mode 100755
index 0000000..4951cd5
--- /dev/null
+++ b/apps/docs/src/modules/cors/index.md
@@ -0,0 +1,181 @@
+# CORS
+
+
+
+
+
+From Wikipedia, Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources on a web page to be accessed from another domain outside the domain from which the first resource was served.
+
+
+New to CORS?
+
+
+
+
+## The CORS Module
+
+Since CORS is a common pain point for web developers, Leaf provides a simple way to deal with most CORS issues. Of course, you can always handle CORS manually, but this module just offers a more convenient and flexible way to do so. It is heavily inspired by the [ExpressJS](https://github.com/expressjs/express) [CORS package](https://github.com/expressjs/cors).
+
+## Installation
+
+You can install CORS through the Leaf CLI.
+
+```bash
+leaf install cors
+```
+
+or with composer
+
+```bash
+composer require leafs/cors
+```
+
+## Using the CORS Module
+
+After installing the cors module, Leaf automatically links it to your app, so it can be used directly on the Leaf instance as the `cors()` method.
+
+
+
+```php
+cors();
+
+// ... your app
+```
+
+
+
+
+```php
+cors();
+
+// ... your app
+```
+
+
+
+::: tip Usage Outside leaf
+Leaf CORS can also be used without leaf's core. You simply need to reference methods on `Leaf\Http\Cors` which is the class for cors.
+
+```php
+Leaf\Http\Cors::config([
+ 'origin' => 'http://example.com',
+ 'optionsSuccessStatus' => 200,
+]);
+```
+
+:::
+
+## Basic usage
+
+When you call `cors()` on your app, it enables CORS for all origins, headers and methods. This is the simplest way to enable CORS on your app.
+
+
+
+```php
+cors();
+
+$app->get('/products/{id}', function () use($app) {
+ $app->response()->json([
+ 'message' => 'This is CORS-enabled for all origins!'
+ ]);
+});
+
+$app->run();
+```
+
+
+
+
+```php
+cors();
+
+app()->get('/products/{id}', function () {
+ response()->json([
+ 'message' => 'This is CORS-enabled for all origins!'
+ ]);
+});
+
+app()->run();
+```
+
+
+
+However, there are times when you might want to be more restrictive by allowing only some origins to access your app. You can do this by passing in an options array to the `cors()` method. This array allows you to configure specific origins, methods, headers, etc. For example, the following code shows how to allow a single origin (http://example.com) to access your app using the `origin` option:
+
+
+
+```php
+$app->cors([
+ 'origin' => 'http://example.com',
+ 'optionsSuccessStatus' => 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
+]);
+```
+
+
+
+
+```php
+app()->cors([
+ 'origin' => 'http://example.com',
+ 'optionsSuccessStatus' => 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
+]);
+```
+
+
+
+A full list of all the options available can be found below.
+
+## Configuration Options
+
+* `origin`: Configures the **Access-Control-Allow-Origin** CORS header. Possible values:
+ * `String` - set `origin` to a specific origin. For example if you set it to `"http://example.com"` only requests from "http://example.com" will be allowed.
+ * `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. For example the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com".
+ * `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com".
+ * `Function` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback (called as `callback(err, origin)`, where `origin` is a non-function value of the `origin` option) as the second.
+* `methods`: Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`).
+* `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header.
+* `exposedHeaders`: Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range') or an array (ex: `['Content-Range', 'X-Content-Range']`). If not specified, no custom headers are exposed.
+* `credentials`: Configures the **Access-Control-Allow-Credentials** CORS header. Set to `true` to pass the header, otherwise it is omitted.
+* `maxAge`: Configures the **Access-Control-Max-Age** CORS header. Set to an integer to pass the header, otherwise it is omitted.
+* `preflightContinue`: Pass the CORS preflight response to the next handler.
+* `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`.
+
+The default configuration is the equivalent of:
+
+```json
+{
+ "origin": "*",
+ "methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
+ "allowedHeaders": "*",
+ "exposedHeaders": "",
+ "credentials": false,
+ "maxAge": null,
+ "preflightContinue": false,
+ "optionsSuccessStatus": 204,
+}
+```
diff --git a/apps/docs/src/modules/date/index.md b/apps/docs/src/modules/date/index.md
new file mode 100755
index 0000000..b00c616
--- /dev/null
+++ b/apps/docs/src/modules/date/index.md
@@ -0,0 +1,494 @@
+
+# Leaf Date
+
+Leaf Date (now known as tick) is a minimalist PHP library that parses, validates, manipulates, and displays dates and times with a largely DayJS/MomentJS-compatible API. If you use DayJS, you already know how to use Tick.
+
+```php
+tick()->now(); // get the current timestamp
+tick()->format('YYYY-MM-DD'); // format the current timestamp
+tick()->startOf('month')->add(1, 'day')->set('year', 2018)->format('YYYY-MM-DD HH:mm:ss');
+```
+
+## Why Tick?
+
+### 3kB
+
+Less PHP to download, parse and execute, leaving more time for your code.
+
+### Simple
+
+Tick is a minimalist PHP library that parses, validates, manipulates, and displays dates and times a largely Day JS and Moment.js compatible API. If you use Day JS or Moment.js, you already know how to use Tick.
+
+## Installation
+
+You can install Tick through Leaf CLI:
+
+```bash
+leaf install date
+```
+
+Or with composer:
+
+```bash
+composer require leafs/date
+```
+
+## Date Operations
+
+The documentation will cover methods divided into 4 categories:
+
+- [Parsing](/modules/date/#parsing)
+- [Manipulation](/modules/date/#manipulation)
+- [Display](/modules/date/#display)
+- [Querying](/modules/date/#querying)
+
+## Parsing
+
+Instead of modifying the native DateTime object, Tick creates a wrapper for the Date object. To get this wrapper object, simply call tick() with one of the supported input types.
+
+```php
+tick('2018-01-01 12:00:00'); // create a date from a string
+tick(); // will use today's date
+```
+
+### The `tick()` function
+
+Calling `tick()` without parameters returns a fresh Tick object. You can then use any of the tick methods to parse, manipulate, or display the date.
+
+```php
+$now = tick()
+```
+
+This is essentially the same as calling `tick('with today\'s date')`.
+
+### String
+
+Parse the given string in ISO 8601 format (a space instead of the 'T' is allowed) and return a Tick object instance.
+
+```php
+tick('2018-04-04T16:00:00.000Z')
+tick('2018-04-13 19:18:17.040+02:00')
+tick('2018-04-13 19:18')
+```
+
+### Date
+
+Create a Tick object with a pre-existing native DateTime object.
+
+```php
+$d = new DateTime();
+tick(d);
+```
+
+## Get + Set
+
+Tick uses overloaded getters and setters, that is to say, calling these methods without parameters acts as a getter, and calling them with a parameter acts as a setter.
+
+These map to the corresponding function on the native DateTime object.
+
+```php
+tick()->second(30); // set the second to 30
+tick()->second(); // get the second
+```
+
+### Millisecond
+
+Gets or sets the millisecond.
+
+Accepts numbers from 0 to 999. If the range is exceeded, it will bubble up to the seconds.
+
+```php
+tick()->millisecond(); // gets current millisecond
+tick()->millisecond(1); // returns new tick o->ject
+```
+
+### Second
+
+Gets or sets the second.
+
+Accepts numbers from 0 to 59. If the range is exceeded, it will bubble up to the minutes.
+
+```php
+tick()->second(); // gets current second
+tick()->second(1); // returns new tick object
+```
+
+### Minute
+
+Gets or sets the minutes.
+
+Accepts numbers from 0 to 59. If the range is exceeded, it will bubble up to the hour.
+
+```php
+tick()->minute(); // gets current minute
+tick()->minute(59); // returns new tick object
+```
+
+### Hour
+
+Gets or sets the hour.
+
+Accepts numbers from 0 to 23. If the range is exceeded, it will bubble up to the day.
+
+```php
+tick()->hour(); // gets current hour
+newDate = tick()->hour(12); // returns new tick object
+```
+
+### Date of Month
+
+Gets or sets the day of the month.
+
+Accepts numbers from 1 to 31. If the range is exceeded, it will bubble up to the months.
+
+```php
+tick()->date(); // gets day of current month
+tick()->date(1); // returns new tick object
+```
+
+### Day of Week
+
+Gets or sets the day of the week.
+
+Accepts numbers from 0 (Sunday) to 6 (Saturday). If the range is exceeded, it will bubble up to other weeks.
+
+```php
+tick()->day(); // gets day of current week
+tick()->day(0); // returns new tick object
+```
+
+### Month
+
+Gets or sets the month.
+
+Accepts numbers from 0 to 11. If the range is exceeded, it will bubble up to the year.
+
+```php
+tick()->month(); // gets current month
+tick()->month(0); // returns new tick object
+```
+
+### Year
+
+Gets or sets the year.
+
+```php
+tick()->year(); // gets current year
+tick()->year(2000); // returns new tick object
+```
+
+### Get
+
+String getter, returns the corresponding information getting from Tick object.
+Units are case insensitive, short forms are case sensitive.
+
+```php
+tick()->get('year');
+tick()->get('month'); // start 0
+tick()->get('date');
+tick()->get('hour');
+tick()->get('minute');
+tick()->get('second');
+tick()->get('millisecond');
+```
+
+#### List of all available units
+
+
+| Unit | Shorthand | Description |
+| ----- | -------- | ----------- |
+| date | D | Date of Month |
+| day | d | Day of Week (Sunday as 0, Saturday as 6) |
+| month | M | Month (January as 0, December as 11) |
+| year | y | Year |
+| hour | h | Hour |
+| minute | m | Minute |
+| second | s | Second |
+| millisecond | ms | Millisecond |
+
+### Set
+
+Generic setter, accepting unit as first argument, and value as second, returns a new instance with the applied changes.
+
+In general:
+
+```php
+tick()->set('date', 1);
+tick()->set('month', 3); // April
+tick()->set('second', 30);
+```
+
+For multiple set:
+
+```php
+tick()->set('hour', 5)->set('minute', 55)->set('second', 15);
+```
+
+## Manipulation
+
+Once you have a Tick object, you may want to manipulate it in some way.
+
+Tick supports method chaining like this:
+
+```php
+tick('2019-01-25')->add(1, 'day')->subtract(1, 'year')->year(2009)->toString()
+```
+
+### Add
+
+Returns a cloned Tick object with a specified amount of time added.
+
+```php
+$a = tick();
+$b = a->add(7, 'day')
+
+// $a -> the original value and will not change
+// $b -> the manipulation result
+```
+
+Units are case insensitive, short forms are case sensitive.
+
+#### List of all available `add` units
+
+| Unit | Shorthand | Description |
+| ------------- | --------- | ---------------------------------------- |
+| `day` | `d` | Day |
+| `week` | `w` | Week |
+| `month` | `M` | Month |
+| `year` | `y` | Year |
+| `hour` | `h` | Hour |
+| `minute` | `m` | Minute |
+| `second` | `s` | Second |
+| `millisecond` | `ms` | Millisecond |
+
+### Subtract
+
+Returns a cloned Tick object with a specified amount of time subtracted.
+
+```php
+tick()->subtract(7, 'year');
+```
+
+Units are case insensitive, and support plural and short forms.
+
+[List of all available units](/modules/date/#list-of-all-available-add-units).
+
+### Start of Time
+
+Returns a cloned Tick object and set it to the start of a unit of time.
+
+```php
+tick()->startOf('year')
+```
+
+Units are case insensitive, and support plural and short forms.
+
+#### List of all available `start` units
+
+| Unit | Shorthand | Description |
+| ------------- | --------- | ---------------------------------------- |
+| `year` | `y` | January 1st, 00:00 this year |
+| `month` | `M` | the first day of this month, 00:00 |
+| `week` | `w` | the first day of this week, 00:00 (locale aware) |
+| `date` | `D` | 00:00 today |
+| `day` | `d` | 00:00 today |
+| `hour` | `h` | now, but with 0 mins, 0 secs, and 0 ms |
+| `minute` | `m` | now, but with 0 seconds and 0 milliseconds |
+| `second` | `s` | now, but with 0 milliseconds |
+
+### End of Time
+
+Returns a cloned Tick object and set it to the end of a unit of time.
+
+```php
+tick()->endOf('month');
+```
+
+The list of all available units is the same as [startOf](/modules/date/#list-of-all-available-start-units).
+
+## Display
+
+Once parsing and manipulation are done, you need some way to display the Tick object.
+
+### Format
+
+Get the formatted date according to the string of tokens passed in.
+
+To escape characters, wrap them in square brackets (e.g. [MM]).
+
+```php
+tick()->format();
+// '2023-04-02T18:04:37+00:00'
+// current date in ISO8601, without fraction seconds
+
+tick('2019-01-25')->format('[YYYYescape] YYYY-MM-DDTHH:mm:ssZ[Z]');
+// 'YYYYescape 2019-01-25T00:00:000Z'
+
+tick('2019-01-25')->format('DD/MM/YYYY'); // '25/01/2019'
+```
+
+#### List of all available formats
+
+| Format | Output | Description |
+| ------ | ------ | ---------------------------------------- |
+| `YY` | `70` | Two-digit year |
+| `YYYY` | `1970` | Four-digit year |
+| `M` | `1-12` | The month, beginning at 1 |
+| `MM` | `01-12` | The month, 2-digits |
+| `MMM` | `Jan-Dec` | The abbreviated month name |
+| `MMMM` | `January-December` | The full month name |
+| `D` | `1-31` | The day of the month |
+| `DD` | `01-31` | The day of the month, 2-digits |
+| `d` | `0-6` | The day of the week, with Sunday as 0 |
+| `dd` | `Su-Sa` | The min name of the day of the week |
+| `ddd` | `Sun-Sat` | The short name of the day of the week |
+| `dddd` | `Sunday-Saturday` | The name of the day of the week |
+| `H` | `0-23` | The hour |
+| `HH` | `00-23` | The hour, 2-digits |
+| `h` | `1-12` | The hour, 12-hour clock |
+| `hh` | `01-12` | The hour, 12-hour clock, 2-digits |
+| `m` | `0-59` | The minute |
+| `mm` | `00-59` | The minute, 2-digits |
+| `s` | `0-59` | The second |
+| `ss` | `00-59` | The second, 2-digits |
+| `SSS` | `000-999` | The millisecond, 3-digits |
+| `Z` | `+01:00` | Offset from UTC, e.g. +01:00 |
+| `ZZ` | `+0100` | Offset from UTC, e.g. +0100 |
+| `A` | `AM` | AM, PM |
+| `a` | `am` | am, pm |
+
+### Time from Now
+
+Returns the string of relative time from now.
+
+@>RelativeTime
+
+```php
+
+tick('2013-01-01')->fromNow(); // 10 years ago
+```
+
+If you pass true, you can get the value without the prefix/suffix.
+
+```php
+
+tick('2013-01-01')->fromNow(true); // 10 years
+```
+
+### Time from X
+
+Returns the string of relative time from X.
+
+```php
+tick('1999-01-01')->from('2000-01-01'); // a year ago
+```
+
+If you pass true, you can get the value without the suffix.
+
+```php
+tick('1999-01-01')->from('2000-01-01', true); // a year
+```
+
+### Time to Now
+
+Returns the string of relative time to now.
+
+```php
+
+tick('2033-01-01')->toNow(); // in 10 years
+```
+
+If you pass true, you can get the value without the prefix/suffix.
+
+```php
+
+tick('2033-01-01')->toNow(true); // 10 years
+```
+
+## Querying
+
+There are several methods to query a Tick object.
+
+### Is Before
+
+This indicates whether the Tick object is before the other supplied date-time.
+
+```php
+tick()->isBefore('2011-01-01');
+```
+
+### Is Same
+
+This indicates whether the Tick object is the same as the other supplied date-time.
+
+```php
+tick()->isSame(new \DateTime('2011-01-01'));
+```
+
+### Is After
+
+This indicates whether the Tick object is after the other supplied date-time.
+
+```php
+tick()->isAfter('2011-01-01');
+```
+
+### Is Between
+
+This indicates whether the Tick object is between two other supplied date-time.
+
+```php
+
+tick('2010-10-20')->isBetween('2010-10-19', new \DateTime('2010-10-25'));
+```
+
+### Is Between Or Equal
+
+This indicates whether the Tick object is between two other supplied date-time or equal to one of them.
+
+```php
+
+tick('2010-10-20')->isBetweenOrEqual('2010-10-19', new \DateTime('2010-10-25'));
+```
+
+### Is same day
+
+This indicates whether the Tick object is the same day as the other supplied date-time.
+
+```php
+tick('2010-10-20')->isSameDay('2010-10-20');
+```
+
+### Is same month
+
+This indicates whether the Tick object is the same month as the other supplied date-time.
+
+```php
+
+
+tick('2010-10-20')->isSameMonth('2010-10-20');
+```
+
+### Is same year
+
+This indicates whether the Tick object is the same year as the other supplied date-time.
+
+```php
+tick('2010-10-20')->isSameYear('2010-10-20');
+```
+
+### Is Leap Year
+
+This indicates whether the Tick object's year is a leap year or not.
+
+```php
+tick('2000-01-01')->isLeapYear(); // true
+```
+
+### Is DateTime
+
+This indicates whether the Tick object is a DateTime object or not.
+
+```php
+tick()->isDateTime('2000-01-01'); // false
+```
diff --git a/apps/docs/src/modules/db-old/index.md b/apps/docs/src/modules/db-old/index.md
new file mode 100755
index 0000000..df788a1
--- /dev/null
+++ b/apps/docs/src/modules/db-old/index.md
@@ -0,0 +1,179 @@
+---
+title: "Leaf Db (Old)"
+---
+
+
+# Leaf DB
+
+This is Leaf's simple query builder created in Leaf v1 but still maintained till date. This class provides a convenient but usual way to quickly create and run database queries. It can be used to perform most database operations in your app. It currently supports Mysqli and PDO connections, though we still recommend using Mysqli. There's no need to worry about SQL injection as parameter binding is also supported and easy to use😉💪
+
+## Installation
+
+You can quickly install leaf db with composer or the leaf cli.
+
+```bash
+composer require leafs/db-old
+```
+
+or with leaf cli:
+
+```bash
+leaf install db-old
+```
+
+## Initialising Leaf DB
+
+Leaf DB has 2 different packages, 1 for mysqli and the other for PDO. So you can import which ever package you wish to use. **Leaf recommends using the mysqli package.**
+
+```php
+use Leaf\Db\Mysqli;
+
+$db = new Mysqli();
+
+use Leaf\Db\PDO;
+
+$db = new PDO();
+```
+
+**Both DB:PDO and DB:Mysqli use the same methods, so all the code below works the same for whichever you're using. We'll alert you if something works differently.**
+
+
+
+## DB connection
+
+The first thing you need to do to use Leaf DB is to connect to your database. This can be achieved with `connect()`
+
+### On the leaf object
+
+In v2.1, the default `$app->db` object has been replaced with `Leaf\Db`, therefore, you have to initialise DB Mysqli to use it's methods.
+
+#### DB Mysqli
+
+```php
+use Leaf\Db\Mysqli;
+
+$db = new Mysqli();
+$db->connect($host, $user, $password, $dbname);
+```
+
+#### DB
+
+```php
+use Leaf\Db\PDO;
+
+$db = new PDO();
+$db->connect($host, $dbname, $user, $password);
+```
+
+This will set the connection for use within Leaf DB.
+
+Both packages now support `auto_connect` which allows you to connect to your database using variables set in a `.env` file.
+
+```php
+$db->auto_connect();
+```
+
+
+
+## Queries
+
+### Making simple queries
+
+Queries with with Leaf DB are much like what you're used to. Though a query builder, we wan't to maintain the flexibility of normal database queries, hence, we provided the query() method to make your normal database queries.
+
+```php
+$db->connect($host, $user, $password, $dbname);
+
+$app->get('/users/all', function () use($app) {
+ $users = $db->query("SELECT username FROM users")->fetchAll();
+ $app->response()->json($users);
+});
+```
+
+As normal as this seems, we take it a step further by providing you with a much simpler way to use prepared statements.
+
+```php
+$db->connect($host, $user, $password, $dbname);
+
+$app->get('/users/{id}', function ($id) use($app) {
+ $user = $db->query("SELECT username FROM users WHERE id = ?", [$id])->fetchObj();
+ $app->response()->json($user);
+});
+```
+
+We've looked at making queries, but then `query()` still makes you type out whatever query you need to use. It's certainly easier than raw queries, but it's nothing impressive. Below are some of Leaf DB's handy methods to make queries even easier.💪😉
+
+### [Retrieving Data](/modules/db-old/select)
+
+Read the [select docs](/modules/db-old/select) for all the information on retrieving data from your database.
+
+### [Inserting Data](/modules/db-old/insert)
+
+Read the [insert docs](/modules/db-old/insert) for all the information on inserting data into your database.
+
+### Updating Data
+
+This operation uses UPDATE. With Leaf DB:
+
+```php
+$db->update();
+```
+
+#### Update
+
+We use Leaf DB's update method which takes in a "table", a "column-value" to update and "conditions".
+
+```php
+$db->update("posts", "title = 'Post 1'", "title = 'Post One'");
+```
+
+This will look for a post with the title of "Post One" and change it to "Post 1".
+You can also have multiple conditions:
+
+```php
+$db->update("posts", "title = 'Post 1' AND author = 'Mychi Darko'", "title = 'Post One'");
+```
+
+##### With Parameter Binding
+
+```php
+$db->update("posts", "title = ? AND author = ?", "title = ?", ["Post 1", "Mychi Darko", "Post One"]);
+```
+
+
+
+### Deleting Data
+
+This operation uses DELETE. With Leaf DB:
+
+```php
+$db->delete();
+```
+
+#### Delete
+
+We use Leaf DB's delete method which takes in a "table", and "conditions".
+
+```php
+$db->delete("posts", "title = 'Post 1'");
+```
+
+This will look for a post with the title of "Post 1" and delete it.
+
+## Others
+
+### Row Count
+
+Get the number of rows from select
+
+```php
+$db->select("posts")->count();
+```
+
+### Connection Close
+
+Close the connection
+
+```php
+$db->close();
+```
diff --git a/apps/docs/src/modules/db-old/insert.md b/apps/docs/src/modules/db-old/insert.md
new file mode 100755
index 0000000..84dad2b
--- /dev/null
+++ b/apps/docs/src/modules/db-old/insert.md
@@ -0,0 +1,101 @@
+---
+aside: none
+---
+
+
+# Inserting Data
+
+Leaf DB has provided really simple, but very helpful methods for inserting data into the database.
+
+
+
+## db insert
+
+### Saving data
+
+We user `$db->insert` to save data in the database. `insert` takes in a "table" to insert data, "column(s)" and "value(s)":
+
+```php
+$db->insert("posts", "title", "This is post One");
+```
+
+You can also add multiple columns like so:
+
+```php
+$db->insert("posts", "title, body", "post One, This is the body of post One");
+```
+
+#### Using Prepared Statements
+
+Prepared statements help protect against SQL injection,...
+
+```php
+$db->insert("posts", "title, body", "?, ?", ["post One, This is the body of post One"]);
+```
+
+
+
+## Db add
+
+`add` simply offers a more consice, powerful way to retrieve data from a database. It also uses prepared statements by default, so you're safe in that respect.
+
+Instead of several parameters in `$db->insert`, `$db->add` takes in an array with key-value pairs to be saved in the database.
+
+```php
+$db->add("posts", ["title" => "Post One", "body" => "This is the body"]);
+
+// $db->add($table, $params_to_insert);
+```
+
+### Uniques
+
+Let's say you want to check whether the username a user just entered has been taken, you'd have to write a bunch of conditional code, making the code count larger and more error prone, right?
+
+Well, `add` solves this problem smoothly. `add` has a 3rd parameter: an array of unique values which makes sure that the same value can't be saved twice.
+
+```php
+$db->add("users", ["name" => "mychi", "email" => "m@m.com", "pass" => "1234"], ["name", "email"]);
+```
+
+So, we're telling `add` to alert us if someone has already registered with the name `mychi` or the email `m@m.com`. This is because we passed `["name", "email"]` as the 3rd param to `add`
+
+**With uniques, you can cut down on your whole app:**
+For instance, if you know the exact data you'll be receiving in your app, let's say a username, email and password from a register form, you can do something like this:
+
+```php
+$app->post("/register", function () use($app, $db) {
+ $db->add("users", $app->request->body(), ["username", "email"]);
+});
+```
+
+So, we pass in the entire request body, which contains the username, email and password. Simple right?
+
+#### Validation
+
+`add` also has inbuilt validation which validates parameters according to set rules. This uses the [`Leaf\Form->validate`](/modules/forms/) method. You can check it out for more information on validation.
+
+`add` takes in a 4th parameter which is a boolean, this is whether of not to validate the data passed into `add` using the default checks.
+
+By default, `add` validates values with the keys: `email`, `username` and any other field is marked as `required`. If any of the validations fail, an error is raised. You can turn this feature off:
+
+```php
+$db->add("posts", ["title" => "Post One", "body" => "..."], ["title"], false);
+```
+
+#### Custom Validation
+
+This is the 5th parameter of `add`. These are custom rules that you set to validate.
+
+```php
+$db->add("posts", ["title" => "Post One", "body" => "..."], ["title"], false, [
+ "author" => "validUsername"
+]);
+```
+
+Here, we're telling `add` that the **author** parameter should be a valid username. If thiscondition(rule) is not met, the application throws an error and breaks.
+
+You can view all validation rules [here](/modules/forms/#validation)
+
+```php
+$db->add($table, $params, $uniques, $defaultChecks, $validation);
+```
diff --git a/apps/docs/src/modules/db-old/model.md b/apps/docs/src/modules/db-old/model.md
new file mode 100755
index 0000000..cbe8309
--- /dev/null
+++ b/apps/docs/src/modules/db-old/model.md
@@ -0,0 +1,40 @@
+# Leaf MVC Models
+
+In LeafMVC, we don't really have anything to do with our models: Leaf Core has taken all the trouble out of using models, so all we have to do in LeafMVC is to generate the model and include it in our controller.
+
+Our Models are kept in `app/models`, but we won't need to create our models manually. Leaf MVC's command line tool covers this for us.
+
+```bash
+php leaf g:model
+```
+
+That's all we need to do with our model. LeafMVC's models have methods prepared which allow us to manipulate out database without doing much.
+
+We can create, read, update and delete without writing any code to specially access our database.
+
+In our controller, we can do these:
+
+```php
+// return all rows
+ModelName::all();
+
+// return all rows sorted by date created
+ModelName::orderBy('created_at', 'desc')->get()
+
+// find a database row by id
+ModelName::find($id);
+
+// find a database row by title
+ModelName::where('title', 'Title goes here')->get();
+
+// create a new database row
+$model = new ModelName;
+$model->field = $this->request->getParam("field");
+$model->save();
+
+// delete a post
+$model = ModelName::find($id);
+$model->delete();
+```
+
+Checkout [building your first app](/docs/introduction/first-app) for more practical use cases
diff --git a/apps/docs/src/modules/db-old/select.md b/apps/docs/src/modules/db-old/select.md
new file mode 100755
index 0000000..a588674
--- /dev/null
+++ b/apps/docs/src/modules/db-old/select.md
@@ -0,0 +1,155 @@
+---
+aside: none
+---
+
+
+# Retrieving Data
+
+If you're attempting to use this, you've probably seen or used `SELECT` statements before. Leaf DB has provided an even easier way to use select.
+
+Leaf has provided a new method to make retrieving data even simpler and more organised. `choose`
+
+## db select
+
+### Getting all rows from a table
+
+To do this, we use the `select()` methode. All that we have to do is to pass in the table we want to retrieve. For example, to get all users from the "users" table, we simply do:
+
+```php
+$db->select("users");
+```
+
+To actually get the results, we'll have to chain `fetchAll()` to the select method. `fetchAll()` does the same thing that `mysqli_fetch_all()` does to an mysqli result
+
+```php
+$db->select("users")->fetchAll();
+```
+
+#### Getting a column from a table
+
+Getting a single column, eg: getting all usernames from the users table
+
+```php
+$db->select("users", "username")->fetchAll();
+```
+
+This is like saying `SELECT username FROM users`. You can also pass in multiple options
+
+```php
+$db->select("users", "username, email")->fetchAll();
+```
+
+You can get all columns with:
+
+```php
+$db->select("users")->fetchAll();
+// or
+$db->select("users", "*")->fetchAll();
+```
+
+#### Getting a particular row from a table
+
+Getting a particular row, eg: getting the user with the id of 1 from the users table. You acan achieve this with:
+
+```php
+$db->select("users", "*", "id = 2")->fetchObj();
+```
+
+`fetchObj` does the same thing as `mysqli_fetch_obj` and `fetch(PDO::FETCH_OBJ)`
+
+If you don't need the whole row, you can use:
+
+```php
+$db->select("users", "username, email", "id = 2")->fetchObj();
+```
+
+#### Limit data
+
+Limiting data is also very simple with Leaf DB
+
+```php
+// get the latest 10 posts
+$users = $db->select("posts ORDER BY id DESC LIMIT 10")->fetchAll();
+
+// with parameters
+$books = $db->select("books", "*", "author = ? ORDER BY id DESC LIMIT 5", [$author])->fetchAll();
+```
+
+#### Using Prepared Statements
+
+Prepared statements help protect against SQL injection,...
+
+```php
+$db->select("users", "*", "username = ? AND password = ?", [$username, $password])->fetchObj();
+```
+
+
+
+## Db choose
+
+`choose` simply offers a more consice, powerful way to retrieve data from a database. It also uses prepared statements by default, so you're safe in that respect.
+
+For basic uses, we'll recommend you use `select`:
+
+```php
+$db->select("users")->fetchAll();
+
+$db->select("users", "username")->fetchAll();
+
+$db->select("users", "username, email")->fetchAll();
+```
+
+### Getting a particular row from a table
+
+Getting a particular row, eg: getting the user with the id of 2 from the users table. You acan achieve this with:
+
+```php
+$db->choose("users", "*", ["id" => 2])->fetchObj();
+```
+
+If you don't need the whole row, you can use:
+
+```php
+$db->choose("users", "username, mobile", ["email" => "mychi@leafphp.dev"])->fetchObj();
+```
+
+#### Data Options
+
+So unlike `select`, choose takes in an array, which is much clearner than writing partial SQL queries, also, the params you pass in are safe from SQL injection.
+
+Also, unlike select, you seperate data options like `LIMIT` and `ORDER` into a 4th parameter
+
+```php
+$db->choose("books", "*", ["author" => "mychi.darko", "published" => "2019"], "LIMIT 5");
+```
+
+#### Validation
+
+`choose` also has inbuilt validation which validates parameters according to set rules. This uses the [`Leaf\Form->validate`](/modules/forms/) method. You can check it out for more information on validation.
+
+`choose` takes in a fifth parameter which is a boolean, this is whether of not to validate the data passed into `choose` using the default checks.
+
+By default, `choose` validates values with the keys: `email`, `username` and any other field is marked as `required`. If any of the validations fail, an error is raised. You can turn this feature off:
+
+```php
+$db->choose("books", "*", ["author" => "mychi.darko", "published" => "2019"], "LIMIT 5", false);
+```
+
+#### Custom Validation
+
+This is the sixth parameter of `choose`. These are custom rules that you set to validate.
+
+```php
+$db->choose("books", "*", ["author" => "mychi.darko", "published" => "2019"], "LIMIT 5", false, [
+ "author" => "validUsername",
+ "published" => "number"
+]);
+```
+
+Here, we're telling `choose` that the **author** parameter should be a valid username, and the **published** param should contain only numbers. If any of these conditions(rules) are not met, the application throws an error and breaks.
+
+You can view all validation rules [here](/modules/forms/#validation)
+
+```php
+$db->choose($table, $fields, $params, $options, $defaultChecks, $validation);
+```
diff --git a/apps/docs/src/modules/db/index.md b/apps/docs/src/modules/db/index.md
new file mode 100755
index 0000000..ba5a113
--- /dev/null
+++ b/apps/docs/src/modules/db/index.md
@@ -0,0 +1,688 @@
+# Getting Started
+
+
+
+
+
+Interacting with a database is a common requirement for most modern web applications. Leaf Db simplifies this process by providing a unified API that supports multiple databases. Leaf Db currently offers first-party support for five databases:
+
+- MariaDB
+- MySQL
+- PostgreSQL
+- SQLite
+- SQL Server
+
+
+Don't understand Databases?
+
+
+
+
+
+
+
+## Installing Leaf Db
+
+You can install Leaf Db with Leaf CLI:
+
+```bash
+leaf install db
+```
+
+Or with composer:
+
+```bash
+composer require leafs/db
+```
+
+From there, you can link your database and start writing some awesome queries.
+
+
+
+## Functional Mode
+
+If you are using leaf db v2 in a leaf 3 app, you will have access to the `db` global which allows you to use Leaf Db from anywhere in your entire application. You simply need to call `db()` and leaf 3 will create and maintain a shared instance of Leaf db which you can call from anywhere.
+
+This also means that you don't need to initialize leaf db anymore.
+
+```php
+connect('127.0.0.1', 'test');
+
+app()->get("/", function () {
+ // db can be used here
+ // db()->...
+});
+
+app()->run();
+```
+
+
+
+## Db Connection
+
+After installing leaf db, you will need to connect to your database to get started. There are multiple ways to connect to your database using leaf db.
+
+
+
+### connect on init
+
+This method connects to the database when initializing Leaf Db.
+
+```php
+// syntax
+$db = new Leaf\Db(
+ $host = '',
+ string $dbname = '',
+ string $user = '',
+ string $password = '',
+ string $dbtype = 'mysql'
+);
+
+// example
+$db = new Leaf\Db('127.0.0.1', 'db_name', 'root', 'password123');
+```
+
+Leaf db takes in 5 optional parameters:
+
+- The database host eg: localhost
+- The database name
+- The database username
+- The database password
+- The PDO database driver eg: mysql, pgsql, ...
+
+Alternatively, you can pass an array into the host parameter to connect to your database like this:
+
+```php
+// syntax
+$db = new Leaf\Db([
+ 'dbtype' => 'mysql',
+ 'charset' => null,
+ 'port' => null,
+ 'unixSocket' => null,
+ 'host' => '127.0.0.1',
+ 'username' => 'root',
+ 'password' => '',
+ 'dbname' => '',
+]);
+
+// example
+$db = new Leaf\Db([
+ 'host' => '127.0.0.1',
+ 'username' => 'root',
+ 'password' => 'password123',
+ 'dbname' => 'db name',
+]);
+```
+
+You only need to pass the fields you want to configure.
+
+::: tip Example sqlite connection
+
+Unlike other database types, SQLite databases are contained within a single file on your filesystem. This means that you don't need to create a database before connecting to it. You can simply pass the path to the database file as the database name.
+
+```php
+$db = new Leaf\Db([
+ 'dbtype' => 'sqlite',
+ 'dbname' => 'db.sqlite',
+]);
+```
+
+:::
+
+
+
+### Manual database connection
+
+Leaf DB ships with a `connect()` method which allows you to connect to your database by passing in the required parameters.
+
+
+
+```php
+$db = new Leaf\Db;
+
+// syntax
+$db->connect(
+ $host = '',
+ string $dbname = '',
+ string $user = '',
+ string $password = '',
+ string $dbtype = 'mysql',
+ array $pdoOptions = []
+);
+
+// example
+$db->connect('127.0.0.1', 'dbname', 'root', '');
+```
+
+Connect works the same way as the constructor, except that it accepts one more parameter: `$pdoOptions` which is a bunch of configuration specific to the PHP `PDO` class.
+
+
+
+
+```php
+// syntax
+db()->connect(
+ $host = '',
+ string $dbname = '',
+ string $user = '',
+ string $password = '',
+ string $dbtype = 'mysql',
+ array $pdoOptions = []
+);
+
+// example
+db()->connect('127.0.0.1', 'dbname', 'root', '');
+```
+
+Leaf db takes in 5 optional parameters:
+
+- The database host eg: localhost
+- The database name
+- The database username
+- The database password
+- The PDO database driver eg: mysql, pgsql, ...
+- Configuration specific to the PHP `PDO` class
+
+
+
+Alternatively, you can pass an array into the host parameter to connect to your database like this:
+
+
+
+You only need to pass the fields you want to configure.
+
+::: tip Example sqlite connection
+
+Unlike other database types, SQLite databases are contained within a single file on your filesystem. This means that you don't need to create a database before connecting to it. You can simply pass the path to the database file as the database name.
+
+
+
+:::
+
+### Auto Connect
+
+Leaf DB also allows you to connect to your database using the database credentials set in your environment variables. This is much easier than having to pass in the credentials every time you want to connect to your database. You can do this using the `autoConnect()` method.
+
+**example env:**
+
+```env
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=LeafMVC
+DB_USERNAME=root
+DB_PASSWORD=
+```
+
+Using the `autoConnect()` method, you can connect to your database like this:
+
+
+
+```php
+db()->autoConnect();
+```
+
+
+
+
+```php
+$db = new Leaf\Db;
+$db->autoConnect();
+```
+
+
+
+### PDO connection
+
+Leaf Db also allows you to skip the entire connection process and share an existing PDO instance with leaf db. This allows you to gradually rewrite your existing apps with Leaf Db without having multiple db connections and doing so at your own pace.
+
+
+
+```php
+$db = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
+
+db()->connection($db);
+
+// you can use leaf db the same way you always have
+```
+
+
+
+
+```php
+$pdo = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
+
+$db = new Leaf\Db();
+$db->connection($pdo);
+
+// you can use leaf db the same way you always have
+```
+
+
+
+## Simple queries
+
+Leaf Db provides a ton of functionality, with a bunch of powerful tools, but at the same time gives you a great deal of customizations with the `query` method. You can write your raw SQL queries with the `query` method, however you can still use the cool features Leaf Db provides.
+
+
+
+You can also use parameter binding with `query`
+
+
+
+```php
+db()->query('SELECT * FROM users WHERE id = ?')->bind('1')->fetchObj();
+```
+
+
+
+
+```php
+$db->query('SELECT * FROM users WHERE id = ?')->bind('1')->fetchObj();
+```
+
+
+
+A shorter method would be to use `where`
+
+
+
+```php
+db()->query('SELECT * FROM users')->where('id', '1')->fetchObj();
+```
+
+
+
+
+```php
+$db->query('SELECT * FROM users')->where('id', '1')->fetchObj();
+```
+
+
+
+You don't have to worry about security, `where` uses prepared statements by default, so you're pretty good.
+
+You've seen all this, but guess what? There's something even shorter
+
+
+
+This is what Leaf Db does for you. A new way to write your Database queries without actually needing to write any real queries. Also, unlike other query builders, there's no need to create classes and models for every table you want to fetch from. Everything's accessible with one line of code.
+
+## Running queries
+
+There are different types of queries, some return values and others don't. Leaf Db provides a seamless way of handling both.
+
+### execute
+
+`execute` is a method on Leaf Db which allows you to run a query instantly. The `execute` method is used when the query is **NOT** expected to return a value.
+
+
+
+### fetchAll
+
+`fetchAll` is a method simply returns all the results of a query. Under the hood, the query is run using `execute` and the value is retrieved and returned. This method is used when there are a lot of values to return.
+
+
+
+::: tip Aliases
+`fetchAll` has aliases adapted from other libraries and frameworks. Instead of `fetchAll`, you can use `all` and `get`
+
+
+
+```php
+$users = db()->query('SELECT * FROM users')->all();
+$users = db()->query('SELECT * FROM users')->get();
+```
+
+
+
+
+```php
+$users = $db->query('SELECT * FROM users')->all();
+$users = $db->query('SELECT * FROM users')->get();
+```
+
+
+
+:::
+
+In this case, the `$users` variable with contain an array of associative arrays, but if you want an array of objects, you can pass `obj` or `object` as a parameter into `fetchAll`
+
+
+
+```php
+$users = db()->query('SELECT * FROM users')->fetchAll('obj');
+$users = db()->query('SELECT * FROM users')->all('object');
+$users = db()->query('SELECT * FROM users')->get('obj');
+```
+
+
+
+
+```php
+$users = $db->query('SELECT * FROM users')->fetchAll('obj');
+$users = $db->query('SELECT * FROM users')->all('object');
+$users = $db->query('SELECT * FROM users')->get('obj');
+```
+
+
+
+### fetchObj
+
+`fetchObj` is a method that fetches the next row and returns it as an object. It returns only one object, so it should be used only on queries that return a single item.
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->fetchObj();
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->fetchObj();
+```
+
+
+
+::: tip Aliases
+Instead of `fetchObj`, you can use `obj`
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->obj();
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->obj();
+```
+
+
+
+:::
+
+::: warning Watch out
+`fetchObj` returns an object, so you can use the result like this:
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->obj();
+$user->id // not $user["id"]
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->obj();
+$user->id // not $user["id"]
+```
+
+
+
+:::
+
+### fetchAssoc
+
+`fetchAssoc` is a method that fetches the next row and returns it as an array. It returns only one array, so it should be used only on queries that return a single item.
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->fetchAssoc();
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->fetchAssoc();
+```
+
+
+
+::: tip Aliases
+Instead of `fetchAssoc`, you can use `assoc`
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->assoc();
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->assoc();
+```
+
+
+
+:::
+
+::: warning Watch out
+`fetchAssoc` returns an array, so you can use the result like this:
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->assoc();
+$user['id'] // not $user->id
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->assoc();
+$user['id'] // not $user->id
+```
+
+
+
+:::
+
+### first
+
+`first` returns the first item in the database that matches the condition given.
+
+
+
+Although all our users are saved in the `users` table, `first` will return only the first record.
+
+### last
+
+`last` returns the last item in the database that matches the condition given.
+
+
+
+Although all our users are saved in the `users` table, `last` will return only the last record.
+
+
diff --git a/apps/docs/src/modules/db/mvc.md b/apps/docs/src/modules/db/mvc.md
new file mode 100644
index 0000000..b2ca169
--- /dev/null
+++ b/apps/docs/src/modules/db/mvc.md
@@ -0,0 +1,19 @@
+# Usage with Leaf MVC
+
+Leaf MVC and Leaf API both come with built-in support for Leaf DB. This means you can skip the initial setup and get right into using Leaf DB. To get started, head over to your `public/index.php` file and uncomment the following line:
+
+```php
+\Leaf\Database::initDb();
+```
+
+This will allow Leaf DB to use your database connection set up in your `.env` file. If you don't have a database connection set up, you can set one up by following the [Leaf MVC Database docs](/docs/leafmvc/). Although you can use Leaf DB in Leaf MVC and Leaf API, they follow the MVC pattern, so we recommend using Models to interact with your database instead of writing queries with Leaf DB. Of course, you should still initialize Leaf DB if you want to use [Leaf Auth](/modules/auth/).
+
+## Models
+
+Leaf DB doesn't come with any pre-defined model classes, so for this, Leaf MVC and Leaf API use Laravel's Eloquent ORM. This means you can use all of Laravel's Eloquent methods on your models. You can find the full docs [here](https://laravel.com/docs/8.x/eloquent).
+
+The `initDb()` method above also syncs Leaf DB with Eloquents' database connection. Of course, you can use your models without calling `initDb()`, but you'll have to set up your Leaf DB connection manually if you want to use [Leaf Auth](/modules/auth/).
+
+## Usage
+
+From this point on, you can use Leaf DB as you would normally. You can find the full docs [here](/modules/db/).
diff --git a/apps/docs/src/modules/db/v/1/index.md b/apps/docs/src/modules/db/v/1/index.md
new file mode 100755
index 0000000..db4e242
--- /dev/null
+++ b/apps/docs/src/modules/db/v/1/index.md
@@ -0,0 +1,520 @@
+---
+title: "Leaf Db v1"
+---
+
+# 💽 Leaf Db
+
+
+Leaf Db is a new lightweight but powerful query builder which allows you quickly write dynamic queries, validate and perform operations on the data in just a single line of code.
+
+You can install Leaf Db with Leaf CLI:
+
+```bash
+leaf install db
+```
+
+Or with composer:
+
+```bash
+composer require leafs/db
+```
+
+From there, you can link your database and start writing some awesome queries.
+
+## Db Connection
+
+The first thing to always do is to connect to your database. Since all db operations are performed on the database, you can't do without it.
+
+There are 3 ways to connect your database.
+
+### connect on init
+
+This method connects to the database when initializing Leaf Db.
+
+```php
+$db = new Leaf\Db("db_host", "user", "password", "db_name");
+```
+
+### connect
+
+Connect takes in 4 params just like the method above
+
+```php
+$db = new Leaf\Db;
+$db->connect("db_host", "user", "password", "db_name");
+```
+
+### autoConnect
+
+This method allows you to connect to your database from parameters in a `.env` file. Most MVC frameworks and other libraries rely on a `.env` for a lot of configurations including the database. With `autoConnect`, you can directly pick up these configs.
+
+**example env:**
+
+```env
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=LeafMVC
+DB_USERNAME=root
+DB_PASSWORD=
+```
+
+**App:**
+
+```php
+$db = new Leaf\Db;
+$db->autoConnect();
+```
+
+## Queries
+
+### Making simple queries
+
+Leaf Db provides a ton of functionality, with a bunch of powerful tools, but at the same time gives you a great deal of customizations with the `query` method. You can write your raw SQL queries with the `query` method, however you can still use the cool features Leaf Db provides.
+
+```php
+$users = $db->query("SELECT * FROM users")->all();
+```
+
+You can also use parameter binding with `query`
+
+```php
+$db->query("SELECT * FROM users WHERE id = ?")->bind("1")->fetchObj();
+```
+
+A shorter method would be to use `where`
+
+```php
+$db->query("SELECT * FROM users")->where("id", "1")->fetchObj();
+```
+
+You don't have to worry about security, `where` uses prepared statements by default, so you're pretty good.
+
+You've seen all this, but guess what? There's something even shorter
+
+```php
+$db->select("users")->where("id", "1")->fetchObj();
+```
+
+This is what Leaf Db does for you. A new way to write your Database queries without actually needing to write any real queries. Also, unlike other query builders, there's no need to create classes and models for every table you want to fetch from. Everything's accessible with one line of code.
+
+## select
+
+As you saw in the example above, `select` makes writing select statements really simple.
+
+It takes in 2 parameters:
+
+- The table to select items from
+- The columns to include (includes all by default)
+
+```php
+// returns all items
+$items = $db->select("items")->all();
+
+// returns the username & email of all buyers
+$buyers = $db->select("buyers", "username, email")->fetchAll();
+```
+
+### where
+
+The where method allows you to quickly write a where block.
+
+```php
+$user = $db->select("users")->where("username", "mychi")->first();
+```
+
+You can also pass in a bunch of params to check for:
+
+```php
+$user = $db->select("users")->where(["username" => "mychi", "password" => "..."])->first();
+```
+
+### orWhere
+
+`orWhere` also functions just like `where`, except that in the case of multiple parameters, `orWhere` returns results even if one of the conditions is met, but `where` only returns results if all the conditions are matched.
+
+```php
+$users = $db->select("users")->orWhere(["username" => "mychi", "username" => "darko"])->all();
+```
+
+### whereLike
+
+`whereLike` is technically the same as `where`, except that instead of comparing stuff "strictly equal", it finds something `like` the value, using the like operator.
+
+```php
+$items = $db->select("items")->whereLike("title", "c%")->all();
+```
+
+This finds any item with a title that starts with c. `%` can be used to modify how the `LIKE` comparism is done, however if you're not sure about the % works, leaf has Db helpers for you.
+
+```php
+// item begins with ...
+whereLike("title", Db::beginsWith("char"))
+
+// item ends with ...
+whereLike("title", Db::endsWith("char"))
+
+// item includes ...
+whereLike("title", Db::includes("char"))
+
+// item starts and ends with ...
+whereLike("title", Db::word("char", "ter"))
+```
+
+### like
+
+This is an alias for `whereLike`. So you can use `like` instead of `whereLike`
+
+### orWhereLike
+
+This combines `orWhere` and `whereLike` in a sense that `orWhereLike` compares using `OR` instead of `AND`, just like `orWhere`, but instead uses the LIKE operator just as `whereLike` does. The interesting thing is that you can combine it with any other where block to make a more complex query.
+
+```php
+$items = $db->select("items")
+ ->where("published", true)
+ ->whereLike("title", $db->beginsWith("sa"))
+ ->orWhereLike("description", $db->beginsWith("sa"))
+ ->all();
+```
+
+### orLike
+
+This is an alias for `orWhereLike`. So you can use `orLike` instead of `orWhereLike`
+
+## Getting your data
+
+After the query is run, the data is returned to leaf db. You can use the methods below to retrieve that data.
+
+### fetchAll
+
+`fetchAll` is a method that's used together with the `select` method. This method simply returns an array consisting of a lot of objects. It is mostly used when querying multiple rows.
+
+```php
+$items = $db->select("items")->fetchAll();
+```
+
+Although the query here is `$db->select("items")`, running just this would return nothing. To actually get the result of this query, you'd need to call `execute`, `fetchObj`, `fetchAssoc` or `fetchAll`
+
+### all
+
+`all` is an alias for `fetchAll`, but is shorter and more familiar with devs who have used other packages. Don't worry, `fetchAll` isn't getting deprecated, you can use it just as you've always done.
+
+### first
+
+`first` returns the first entity of all matching results for a certain query.
+
+```php
+function getFirstItem()
+{
+ // ...
+ return $db->select("items")->first();
+}
+```
+
+### last
+
+`last` returns the last entity of all matching results for a certain query.
+
+```php
+function getLastItem()
+{
+ // ...
+ return $db->select("items")->last();
+}
+```
+
+### execute
+
+This method is used on queries which don't return anything like insert, update and delete queries. This method just runs the desired query and returns `void`, however, if there is a problem, it returns `null`. You can then call `$db->errors()` to get the exact error.
+
+From v2.4-beta up, execute takes in an **optional** parameter, the type of values passed into `bind`, `params` or `where`
+
+```php
+$db->insert("users")->params(["username" => "mychi"])->execute("s");
+```
+
+### fetchObj
+
+This is just like `fetchAll` except that fetchObj is used on select queries usually involving one row
+
+```php
+$db->select("users")->where("id", "1")->fetchObj();
+```
+
+If `fetchAll` is used in this case, the result would look something like this:
+
+```php
+[
+ [
+ "id" => "1"
+ ]
+]
+```
+
+Also, note that `fetchObj` returns an object, so you can use the result like this
+
+```php
+$user = $db->select("users")->where("id", "1")->fetchObj();
+$user->id // not $user["id"]
+```
+
+### fetchAssoc
+
+This is just like the `fetchObj` method, except that it returns an associative array, not an object.
+
+```php
+$user = $db->select("users")->where("id", "1")->fetchAssoc();
+$user["id"]; // not $user->id
+```
+
+## Table operations
+
+### table
+
+`table` sets the table pointer for the db table being used. `table` can be combined with other methods like `search`.
+
+```php
+$db->table("items");
+```
+
+### search
+
+Just as the name implies, you can use this method to search for a value in the database table. It is used with the `table` method.
+
+```php
+$res = $db->table("items")->search("name", "chocola");
+```
+
+This will try to find an item which has chocola in it's name field.
+
+## insert
+
+`Insert` provides a much simpler syntax for making insert queries.
+
+```php
+$db->insert("users") // faster than $db->query("INSERT INTO users")
+```
+
+### params
+
+This method is used on `insert` and `update` just like how `where` is used on `select` and `delete`.
+
+```php
+$db->insert("users")->params("username", "mychi");
+```
+
+To actually run this query, you have to call `execute`.
+
+```php
+$db->insert("users")->params("username", "mychi")->execute();
+```
+
+This inserts a user with a username of mychi into the users table. But what if you wanted to add more params, simple!
+
+```php
+$db->insert("users")->params([
+ "username" => "mychi",
+ "email" => "mychi@leafphp.dev"
+])->execute();
+```
+
+You're free to arrange this query anyhow you see fit, it's still considered as a single chain.
+
+```php
+$db->insert("users")
+ ->params([
+ "username" => "mychi",
+ "email" => "mychi@leafphp.dev",
+ "password" => md5("test")
+ ])
+ ->execute();
+```
+
+What if you already registered someone with the username mychi, this tiny flaw could break your authentication system. That's where `unique` comes in🧐
+
+### unique
+
+Just as the name implies, `unique` helps prevent duplicates in your database, fun fact, just chain one more method for this functionality🤗
+
+```php
+$db->insert("users")
+ ->params([
+ "username" => "mychi",
+ "email" => "mychi@leafphp.dev",
+ "password" => md5("test")
+ ])
+ ->unique("username", "email")
+ ->execute();
+```
+
+If you have a 100 unique values, don't feel shy, just line them all up.
+
+```php
+->unique("username", "email", "what-not", ...)
+```
+
+Alternatively, you could just pack a truck load full of uniques in an array
+
+```php
+->unique(["username", "email", "what-not", ...])
+```
+
+## update
+
+Quickly write an update query.
+
+```php
+$db->update("users")->params("location", "Ghana")->where("id", "1")->execute();
+```
+
+This is generally how an update looks like. Just like with insert, you can add up uniques to make sure you don't have duplicates in your database.
+
+**you can chain in unique here as well.**
+
+## delete
+
+Let's jump straight in for an example.
+
+```php
+$db->delete("users")->execute();// careful now🙂
+```
+
+This code above, ladies and gentlemen, will wipe all your users resulting in 7 digit loses🤞
+
+```php
+$db->delete("users")->where("id", "1")->execute();
+```
+
+You have succesfully deleted user 1
+
+## Extras
+
+At this point, there's still a whole lot you can do with Leaf Db.
+
+There are times when you have to insert data you don't know about. What happens if your user enters unsupported info. To fix this, you'll have to run a bunch of checks to find out what kind of information is being saved, but what if you could validate data before saving without writing any extensive validation? Well...prepare to be amazed🧐
+
+### validate
+
+Validate makes sure that correct information is saved in your database. You simply need to chain the `validate` method.
+
+```php
+$db->insert("users")
+ ->params([
+ "username" => "mychi",
+ "email" => "mychi@leafphp.dev",
+ "password" => md5("test")
+ ])
+ ->validate("username", "validUsername")
+ ->execute();
+```
+
+Validate takes in 2 parameters, a field to validate and a validation rule. You can find all the validation rules and what they do [here](/modules/forms/#multiple-rule-validation). So what if you need to validate more than 1 parameter?
+
+```php
+$db->insert("users")
+ ->params([
+ "username" => "mychi",
+ "email" => "mychi@leafphp.dev",
+ "password" => md5("test")
+ ])
+ ->validate([
+ "username" => "validUsername",
+ "email" => "email"
+ ])
+ ->execute();
+```
+
+Amazing right?!
+
+### hidden
+
+Not all information which is retrieved from the database is sent over to the client side or is added to the session or cookies. Usually, some fields are left out for "security" reasons. `hidden` returns the retrieved data without the `hidden` fields.
+
+```php
+$db->select("users")->hidden("remember_token", "reset_q_id")->fetchAll();
+```
+
+```php
+$db->select("users")->where("id", "1")->hidden("remember_token", "reset_q_id")->fetchObj();
+```
+
+### add
+
+That's right, just imagine doing the opposite of `hidden`, instead of hiding fields from the query data, `add` lets you add your own fields into the query data.
+
+::: tip NOTE
+This does not touch your database, it only appends a field into the data returned from the database.
+:::
+
+```php
+$db->select("users")->add("tx_id", gID())->fetchAll();
+```
+
+This query adds a `tx_id` field with a value generated from `gID` to every user
+
+```php
+$db->select("users")->where("id", "1")->add("tx_id", "d362d7t2366")->fetchObj();
+```
+
+This is similar as the query above, except that this query is on the scale of a single user.
+
+### bind
+
+We've already seen `bind` in action, but we've not actually talked about it. This method allows you to bind parameters into your query.
+
+```php
+$db->select("users WHERE username = ?")->bind("mychi")->fetchAssoc();
+```
+
+And yet again another syntax🧐 As said above, Leaf Db is highly customizable, and allows you to write queries in a way that suits you. This statement above binds `mychi` to the username.
+
+```php
+$db->select("users WHERE username = ? AND password = ?")->bind("mychi", "password")->fetchAssoc();
+```
+
+You can just pass multiple parameters into bind, as many as satisfy your query. If you feel more comfortable with arrays, you can use arrays.
+
+```php
+$db->select("users WHERE username = ? AND password = ?")->bind(["mychi", "password"])->fetchAssoc();
+```
+
+### orderBy
+
+orderBy allows you to arrange the query results according to a row, in ascending (asc) or descending (desc) order.
+
+```php
+// if second param is not provided, desc is used by default
+$items = $db->select("items")->orderBy("created_at")->all();
+
+... orderBy("id", "desc")->all();
+```
+
+### limit
+
+When retrieving data from your database for use in applications, you might want to show only a specific number of values.
+
+```php
+$itemsPerPage = 15;
+$items = $db->select("items")->limit($itemsPerPage)->fetchAll();
+
+// you can use limit and orderBy together
+$items = $db->select("items")->orderBy("id", "desc")->limit($itemsPerPage)->fetchAll();
+```
+
+### error handling
+
+Errors come up all the time, user errors, that is. What happens when validation fails, or if someone has already registered a username. Leaf Db provides a simple way to track these errors.
+
+```php
+$res = $db->insert("users")->params("username", "mychi")->unique("username")->execute();
+if ($res === false) $app->response->exit($db->errors());
+```
+
+Using `$db->errors()` returns an array holding any errors which caused the query to fail. eg:
+
+```php
+[
+ "email" => "email already exists",
+ "username" => "username can only contain characters 0-9, A-z and _"
+]
+```
diff --git a/apps/docs/src/modules/db/v/2/builder.md b/apps/docs/src/modules/db/v/2/builder.md
new file mode 100644
index 0000000..7cadfcc
--- /dev/null
+++ b/apps/docs/src/modules/db/v/2/builder.md
@@ -0,0 +1,820 @@
+# Building Queries
+
+As demonstrated [before](/modules/db/v/2/#simple-queries), Leaf Db allows you to perfectly write SQL queries, however, it also provides simpler and more convenient methods for building queries. This means that you won't need to write any SQL statements.
+
+## create
+
+
+This method allows you build a query to create a database.
+
+
+
+## select
+
+This is a method for quickly building select statements. The `SELECT` statement is used to select data from a database.
+
+It takes in 2 parameters:
+
+- The table to select items from
+- The columns to include (includes all by default)
+
+
+
+```php
+// returns all items
+$items = db()->select('items')->all();
+
+// returns the username & email of all buyers
+$buyers = db()->select("buyers", "username, email")->fetchAll();
+```
+
+
+
+
+```php
+// returns all items
+$items = $db->select('items')->all();
+
+// returns the username & email of all buyers
+$buyers = $db->select("buyers", "username, email")->fetchAll();
+```
+
+
+
+### where
+
+The where method allows you to quickly write a where block.
+
+
+
+#### Comparators
+
+
+v2 of leaf db introduces a third parameter to the `where` and `orWhere` blocks. This allows you to check how a value compares to another using `=`, `LIKE`, `>`, ...
+
+
+
+### orWhere
+
+`orWhere` also functions just like `where`, except that in the case of multiple parameters, `orWhere` returns results even if one of the conditions is met, but `where` only returns results if all the conditions are matched.
+
+
+
+Kind of like `SELECT * FROM users WHERE username = 'mychi' OR username = 'darko'`
+
+::: tip Chaining
+You can chain `where` and `orWhere` blocks together to make queries that use `AND` and `OR` operators.
+
+
+
+This query will look for a username which is either `mychi` or `darko` and return whichever it finds first.
+
+:::
+
+### find
+
+This method allows you to quickly perform a check for the `id` key on a table. It takes in 1 parameter which is the id of the row to get.
+
+
+
+::: tip Note
+Find returns the value it finds immediately, so you should not use `fetchAssoc` or any other fetch method on the value returned.
+:::
+
+## Table operations
+
+### table
+
+`table` sets the table pointer for the db table being used. `table` can be combined with other methods like `search`.
+
+
+
+```php
+db()->table("items");
+```
+
+
+
+
+```php
+$db->table("items");
+```
+
+
+
+### search
+
+Just as the name implies, you can use this method to search for a value in the database table. It is used with the `table` method.
+
+
+
+This will try to find an item which has chocola in it's name field.
+
+## insert
+
+`Insert` provides a much simpler syntax for making insert queries.
+
+
+
+```php
+db()->insert("users") // faster than db()->query("INSERT INTO users")
+```
+
+
+
+
+```php
+$db->insert("users") // faster than db()->query("INSERT INTO users")
+```
+
+
+
+### params
+
+This method is used on `insert` and `update` just like how `where` is used on `select` and `delete`.
+
+
+
+What if you already registered someone with the username mychi, this tiny flaw could break your authentication system. That's where `unique` comes in🧐
+
+### unique
+
+Just as the name implies, `unique` helps prevent duplicates in your database, fun fact, just chain one more method for this functionality
+
+
+
+If you have a 100 unique values, don't feel shy, just line them all up.
+
+```php
+->unique('username', 'email', 'what-not', ...)
+```
+
+Alternatively, you could just pack a truck load full of uniques in an array
+
+```php
+->unique(['username', 'email', 'what-not', ...])
+```
+
+### Getting the last inserted id
+
+You can get the last inserted id by calling `lastInsertId` on the db object after an insert query.
+
+
+
+This is generally how an update looks like. Just like with insert, you can add up uniques to make sure you don't have duplicates in your database.
+
+**you can chain in unique here as well.**
+
+## delete
+
+Let's jump straight in for an example.
+
+
+
+```php
+db()->delete("users")->execute(); // careful now 🙂
+```
+
+
+
+
+```php
+$db->delete("users")->execute(); // careful now 🙂
+```
+
+
+
+::: danger Watch out
+Be careful when running `delete` queries without a `where` block. Doing that will wipe that whole table.
+:::
+
+
+
+You have succesfully deleted user 1
+
+## Extras
+
+### hidden
+
+Not all information which is retrieved from the database is sent over to the client side or is added to the session or cookies. Usually, some fields are left out for "security" reasons. `hidden` returns the retrieved data without the `hidden` fields.
+
+
+
+### add
+
+That's right, just imagine doing the opposite of `hidden`, instead of hiding fields from the query data, `add` lets you add your own fields into the query data.
+
+::: tip NOTE
+This does not touch your database, it only appends a field into the data returned from the database.
+:::
+
+
+
+This is similar as the query above, except that this query is on the scale of a single user.
+
+### bind
+
+We've already seen `bind` in action, but we've not actually talked about it. This method allows you to bind parameters into your query.
+
+
+
+And yet again another syntax🧐 As said above, Leaf Db is highly customizable, and allows you to write queries in a way that suits you. This statement above binds `mychi` to the username.
+
+
+
+### orderBy
+
+orderBy allows you to arrange the query results according to a row, in ascending (asc) or descending (desc) order.
+
+
+
+```php
+// if second param is not provided, desc is used by default
+$items = db()->select("items")->orderBy("created_at")->all();
+
+... orderBy("id", "desc")->all();
+```
+
+
+
+
+```php
+// if second param is not provided, desc is used by default
+$items = $db->select("items")->orderBy("created_at")->all();
+
+... orderBy("id", "desc")->all();
+```
+
+
+
+### limit
+
+When retrieving data from your database for use in applications, you might want to show only a specific number of values.
+
+
+
+```php
+$itemsPerPage = 15;
+$items = db()->select("items")->limit($itemsPerPage)->fetchAll();
+
+// you can use limit and orderBy together
+$items = db()
+ ->select("items")
+ ->orderBy("id", "desc")
+ ->limit($itemsPerPage)
+ ->fetchAll();
+```
+
+
+
+
+```php
+$itemsPerPage = 15;
+$items = $db->select("items")->limit($itemsPerPage)->fetchAll();
+
+// you can use limit and orderBy together
+$items = $db
+ ->select("items")
+ ->orderBy("id", "desc")
+ ->limit($itemsPerPage)
+ ->fetchAll();
+```
+
+
+
+### error handling
+
+Errors come up all the time, user errors, that is. What happens when validation fails, or if someone has already registered a username. Leaf Db provides a simple way to track these errors.
+
+
+
+Using `db()->errors()` returns an array holding any errors which caused the query to fail. eg:
+
+```php
+[
+ "email" => "email already exists",
+ "username" => "username can only contain characters 0-9, A-z and _"
+]
+```
diff --git a/apps/docs/src/modules/db/v/2/index.md b/apps/docs/src/modules/db/v/2/index.md
new file mode 100755
index 0000000..a14c818
--- /dev/null
+++ b/apps/docs/src/modules/db/v/2/index.md
@@ -0,0 +1,619 @@
+# Getting Started
+
+
+## Installation
+
+You can install Leaf Db with Leaf CLI:
+
+```bash
+leaf install db
+```
+
+Or with composer:
+
+```bash
+composer require leafs/db
+```
+
+From there, you can link your database and start writing some awesome queries.
+
+::: tip Coming from v1
+If you are coming from Leaf Db v1, we recommend checking the [changelog](/modules/db/v/2/new)
+:::
+
+
+
+## Functional Mode
+
+If you are using leaf db v2 in a leaf 3 app, you will have access to the `db` global which allows you to use Leaf Db from anywhere in your entire application. You simply need to call `db()` and leaf 3 will create and maintain a shared instance of Leaf db which you can call from anywhere.
+
+This also means that you don't need to initialize leaf db anymore.
+
+```php
+connect('127.0.0.1', 'test');
+
+app()->get("/", function () {
+ // db can be used here
+ // db()->...
+});
+
+app()->run();
+```
+
+
+
+## Db Connection
+
+After installing leaf db, you will need to connect to your database to get started. There are multiple ways to connect to your database using leaf db.
+
+
+
+### connect on init
+
+This method connects to the database when initializing Leaf Db.
+
+```php
+// syntax
+$db = new Leaf\Db(
+ $host = '',
+ string $dbname = '',
+ string $user = '',
+ string $password = '',
+ string $dbtype = 'mysql'
+);
+
+// example
+$db = new Leaf\Db('127.0.0.1', 'db_name', 'root', 'password123');
+```
+
+Leaf db takes in 5 optional parameters:
+
+- The database host eg: localhost
+- The database name
+- The database username
+- The database password
+- The PDO database driver eg: mysql, pgsql, ...
+
+Alternatively, you can pass an array into the host parameter to connect to your database like this:
+
+```php
+// syntax
+$db = new Leaf\Db([
+ 'dbtype' => 'mysql',
+ 'charset' => null,
+ 'port' => null,
+ 'unixSocket' => null,
+ 'host' => '127.0.0.1',
+ 'username' => 'root',
+ 'password' => '',
+ 'dbname' => '',
+]);
+
+// example
+$db = new Leaf\Db([
+ 'host' => '127.0.0.1',
+ 'username' => 'root',
+ 'password' => 'password123',
+ 'dbname' => 'db name',
+]);
+```
+
+You only need to pass the fields you want to configure.
+
+
+
+### connect
+
+Connect takes in 4 params just like the method above
+
+
+
+```php
+// syntax
+db()->connect(
+ $host = '',
+ string $dbname = '',
+ string $user = '',
+ string $password = '',
+ string $dbtype = 'mysql',
+ array $pdoOptions = []
+);
+
+// example
+db()->connect('127.0.0.1', 'dbname', 'root', '');
+```
+
+Leaf db takes in 5 optional parameters:
+
+- The database host eg: localhost
+- The database name
+- The database username
+- The database password
+- The PDO database driver eg: mysql, pgsql, ...
+- Configuration specific to the PHP `PDO` class
+
+
+
+
+```php
+$db = new Leaf\Db;
+
+// syntax
+$db->connect(
+ $host = '',
+ string $dbname = '',
+ string $user = '',
+ string $password = '',
+ string $dbtype = 'mysql',
+ array $pdoOptions = []
+);
+
+// example
+$db->connect('127.0.0.1', 'dbname', 'root', '');
+```
+
+Connect works the same way as the constructor, except that it accepts one more parameter: `$pdoOptions` which is a bunch of configuration specific to the PHP `PDO` class.
+
+
+
+Alternatively, you can pass an array into the host parameter to connect to your database like this:
+
+
+
+You only need to pass the fields you want to configure.
+
+### autoConnect
+
+This method allows you to connect to your database from parameters in a `.env` file. Most MVC frameworks and other libraries rely on a `.env` for a lot of configurations including the database. With `autoConnect`, you can directly pick up these configs.
+
+**example env:**
+
+```env
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=LeafMVC
+DB_USERNAME=root
+DB_PASSWORD=
+```
+
+**App:**
+
+
+
+```php
+db()->autoConnect();
+```
+
+
+
+
+```php
+$db = new Leaf\Db;
+$db->autoConnect();
+```
+
+
+
+### PDO connection
+
+Leaf Db also allows you to skip the entire connection process and share an existing PDO instance with leaf db. This allows you to gradually rewrite your existing apps with Leaf Db without having multiple db connections and doing so at your own pace.
+
+
+
+```php
+$db = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
+
+db()->connection($db);
+
+// you can use leaf db the same way you always have
+```
+
+
+
+
+```php
+$pdo = new PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
+
+$db = new Leaf\Db();
+$db->connection($pdo);
+
+// you can use leaf db the same way you always have
+```
+
+
+
+## Simple queries
+
+Leaf Db provides a ton of functionality, with a bunch of powerful tools, but at the same time gives you a great deal of customizations with the `query` method. You can write your raw SQL queries with the `query` method, however you can still use the cool features Leaf Db provides.
+
+
+
+You can also use parameter binding with `query`
+
+
+
+```php
+db()->query('SELECT * FROM users WHERE id = ?')->bind('1')->fetchObj();
+```
+
+
+
+
+```php
+$db->query('SELECT * FROM users WHERE id = ?')->bind('1')->fetchObj();
+```
+
+
+
+A shorter method would be to use `where`
+
+
+
+```php
+db()->query('SELECT * FROM users')->where('id', '1')->fetchObj();
+```
+
+
+
+
+```php
+$db->query('SELECT * FROM users')->where('id', '1')->fetchObj();
+```
+
+
+
+You don't have to worry about security, `where` uses prepared statements by default, so you're pretty good.
+
+You've seen all this, but guess what? There's something even shorter
+
+
+
+This is what Leaf Db does for you. A new way to write your Database queries without actually needing to write any real queries. Also, unlike other query builders, there's no need to create classes and models for every table you want to fetch from. Everything's accessible with one line of code.
+
+## Running queries
+
+There are different types of queries, some return values and others don't. Leaf Db provides a seamless way of handling both.
+
+### execute
+
+`execute` is a method on Leaf Db which allows you to run a query instantly. The `execute` method is used when the query is **NOT** expected to return a value.
+
+
+
+### fetchAll
+
+`fetchAll` is a method simply returns all the results of a query. Under the hood, the query is run using `execute` and the value is retrieved and returned. This method is used when there are a lot of values to return.
+
+
+
+::: tip Aliases
+`fetchAll` has aliases adapted from other libraries and frameworks. Instead of `fetchAll`, you can use `all` and `get`
+
+
+
+```php
+$users = db()->query('SELECT * FROM users')->all();
+$users = db()->query('SELECT * FROM users')->get();
+```
+
+
+
+
+```php
+$users = $db->query('SELECT * FROM users')->all();
+$users = $db->query('SELECT * FROM users')->get();
+```
+
+
+
+:::
+
+In this case, the `$users` variable with contain an array of associative arrays, but if you want an array of objects, you can pass `obj` or `object` as a parameter into `fetchAll`
+
+
+
+```php
+$users = db()->query('SELECT * FROM users')->fetchAll('obj');
+$users = db()->query('SELECT * FROM users')->all('object');
+$users = db()->query('SELECT * FROM users')->get('obj');
+```
+
+
+
+
+```php
+$users = $db->query('SELECT * FROM users')->fetchAll('obj');
+$users = $db->query('SELECT * FROM users')->all('object');
+$users = $db->query('SELECT * FROM users')->get('obj');
+```
+
+
+
+### fetchObj
+
+`fetchObj` is a method that fetches the next row and returns it as an object. It returns only one object, so it should be used only on queries that return a single item.
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->fetchObj();
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->fetchObj();
+```
+
+
+
+::: tip Aliases
+Instead of `fetchObj`, you can use `obj`
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->obj();
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->obj();
+```
+
+
+
+:::
+
+::: warning Watch out
+`fetchObj` returns an object, so you can use the result like this:
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->obj();
+$user->id // not $user["id"]
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->obj();
+$user->id // not $user["id"]
+```
+
+
+
+:::
+
+### fetchAssoc
+
+`fetchAssoc` is a method that fetches the next row and returns it as an array. It returns only one array, so it should be used only on queries that return a single item.
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->fetchAssoc();
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->fetchAssoc();
+```
+
+
+
+::: tip Aliases
+Instead of `fetchAssoc`, you can use `assoc`
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->assoc();
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->assoc();
+```
+
+
+
+:::
+
+::: warning Watch out
+`fetchAssoc` returns an array, so you can use the result like this:
+
+
+
+```php
+$user = db()->query('SELECT * FROM users WHERE id = 1')->assoc();
+$user['id'] // not $user->id
+```
+
+
+
+
+```php
+$user = $db->query('SELECT * FROM users WHERE id = 1')->assoc();
+$user['id'] // not $user->id
+```
+
+
+
+:::
+
+### first
+
+`first` returns the first item in the database that matches the condition given.
+
+
+
+Although all our users are saved in the `users` table, `first` will return only the first record.
+
+### last
+
+`last` returns the last item in the database that matches the condition given.
+
+
+
+Although all our users are saved in the `users` table, `last` will return only the last record.
+
+
diff --git a/apps/docs/src/modules/db/v/2/new.md b/apps/docs/src/modules/db/v/2/new.md
new file mode 100644
index 0000000..12b9e16
--- /dev/null
+++ b/apps/docs/src/modules/db/v/2/new.md
@@ -0,0 +1,344 @@
+# New in v2
+
+Leaf db has gone through a complete rewrite to bring you the best experience possible in the simplest way for a db library. This page lists all the changes that have gone on in leaf db: both internal and external changes.
+
+## PDO rewrite
+
+Under the hood, Leaf DB has been rewritten to fully support PDO, both internally and user instantiated PDO instances. This makes leaf db more flexible and more compatible with most systems and applications.
+
+Because of this, you can pass in an existing PDO connection, and leaf db should work fine, even without initializing a leaf db connection.
+
+
+
+```php
+connection($db);
+
+// you can use leaf db here with calling db()->connect
+db()->select(...)..;
+```
+
+
+
+
+```php
+connection($pdo);
+
+// you can use leaf db here with calling $db->connect
+$db->select(...)...;
+```
+
+
+
+This allows you to gradually rewrite your existing applications with leaf db, one query at a time.
+
+## DB Support
+
+Rewriting leaf db with PDO now allows you to connect to different database types like SQLite and PostgreSQL. Since any valid PDO connection is a valid leaf db connection, leaf db supports all db types supported by PDO.
+
+## Deep syncing with leaf 3
+
+Leaf DB is now detached from leaf, however, as a leaf 3 module, there's additional functionality you can get from using leaf db in a leaf 3 app. Deep syncing config, instances and functional mode all become available to you.
+
+This simply means that your db config becomes available on `app()->config()`, as well as functional mode seen in the examples above.
+
+## Performance Improvements
+
+Despite switching to PDO and huggling a lot of operations under the hood to provide the best user experience, we put measures in place to optimize performance. After running a bunch of benchmarks in the same app, leaf db v2 performs up to 2x better than v1.
+
+## Db create method
+
+`create` is a much requested feature which did not exist in previous versions. This method allows you to quickly and seamlessly build a query to create a database.
+
+
+
+## Db drop method
+
+A create method should come with a `drop` method, and this version does. This method allows you to quickly drop a database.
+
+
+
+## `first` and `last` methods
+
+These methods are used to return the first and last values matching a given condition respectively. In db v1, these methods would retrieve all records matching the condition but only return the first/last value. For large pools of data, this is simply inefficient. This problem has been solved by modifying the query to fetch only the needed value directly from the database.
+
+## Breaking changes
+
+To give you the best experience, a few things had to change under the hood. This section is intended to document all changes that may lead to your application breaking after upgrading leaf db.
+
+### DB CONNECTION
+
+Although the connect method still does the same things, we shifted things up a bit to accomodate a few internal changes. The `connect` method's parameters now come in a different order.
+
+
+
+As you've noticed, `$pdoOptions` is a new parameter which stems from rewriting leaf db with PDO. Also, the position of the dbname has been changed to the second parameter. Quickly moving your dbname to the second parameter should solve any problems you'll encounter connecting to your database.
+
+
+
+This same thing applies to the leaf db constructor.
+
+```php
+// syntax
+$db = new Leaf\Db(
+ $host = '',
+ string $dbname = '',
+ string $user = '',
+ string $password = '',
+ string $dbtype = 'mysql'
+);
+
+// example
+$db = new Leaf\Db('127.0.0.1', 'dbname', 'root', '');
+```
+
+
+
+### WHERE BLOCKS
+
+Leaf db v1 had a unique way of handling multiple where blocks which wasn't exactly the nicest thing to do. Look at this query for instance.
+
+
+
+You notice we use 3 different types of `where`, and some queries could require more. This significantly reduces the developer experience and adds a bit more to the learning curve.
+
+`where`, `whereLike`, `orWhere`, `orWhereLike` have been replaced with `where` and `orWhere` in v2. This is due to the addition of a third parameter which contains the comparator for the query.
+
+
+
+In this case, we are using `NOT LIKE` instead of `LIKE`.
+
+::: danger Where dependents
+Since some `where` functions have been removed, any functions which depend on them have also been removed. This means that functions like `like` and `orLike` have also been removed.
+:::
+
+### VALIDATE
+
+The validate method, originally powered by leaf forms has also been removed. You can directly use leaf forms to validate your data if you want to do so.
+
+### SEARCH UTILS
+
+The search helpers `beginsWith`, `endsWith`, `includes` and `word` have been moved to `Leaf\Db\Utils`.
+
+
diff --git a/apps/docs/src/modules/devtools/index.md b/apps/docs/src/modules/devtools/index.md
new file mode 100644
index 0000000..2c57185
--- /dev/null
+++ b/apps/docs/src/modules/devtools/index.md
@@ -0,0 +1,127 @@
+# Leaf DevTools
+
+Leaf DevTools provides a set of tools for debugging and understanding your Leaf applications. At the Core, the DevTools provides a visual tool with a clean and intuitive UI holding information about your Leaf application, and a light-weight library that you can use to interact with the devtools frontend.
+
+
+
+## Installation
+
+You can install the Leaf DevTools using the Leaf CLI:
+
+```bash
+leaf install devtools
+```
+
+Or with composer:
+
+```bash
+composer require leafs/devtools
+```
+
+After installing the devtools module, you need to add the hook to your app. This will register the devtools routes and allow your Leaf app to communicate with the DevTools. You can do this by adding this line to your app root.
+
+
+
+```php{7}
+
+
+
+
+```php{5}
+
+
+## Basic Usage
+
+Once you have completed the process above, you can start your application and open the DevTools in your browser. You can do this by going to `http://localhost:port/leafDevTools` in your browser.
+
+::: tip Dev Experience
+Leaf DevTools are still being developed. We're working on making the experience better and more intuitive. If you have any suggestions, please feel free to open an issue on the [GitHub repo](https://github.com/leafsphp/devtools).
+:::
+
+## Application Insights
+
+Leaf DevTools has an insights tab that provides information about your Leaf app, like your application config, routes, cookies, sessions, env and more. This information is useful for debugging and understanding your app and why it behaves the way it does.
+
+
+
+## Installed Packages
+
+On the packages tab, you can see all the installed packages in your application. The installed packages are separated into two categories: Composer packages and Leaf packages. You can also see the version of each package, a description and a link to the package's GitHub repo.
+
+
+
+## Server Console Logs
+
+The server module allows you to log out data which will be displayed in the dev tools console (just like console.log). Since PHP doesn't have any real implementation of something like JavaScript's console.log, we decided to add something like that as it is useful for debugging.
+
+To get started, call the console method on `Leaf\DevTools`
+
+```php
+\Leaf\DevTools::console('This data should be logged in the console');
+\Leaf\DevTools::console('This is a warning', 'warn');
+\Leaf\DevTools::console('This is an error', 'error');
+\Leaf\DevTools::console('This is an info message', 'info');
+\Leaf\DevTools::console('This is a debug message', 'log');
+```
+
+
+
+## Application Routes
+
+The routes tab shows all the routes in your application. It shows the route's name, method, path, handler and middleware if available.
+
+
+
+## Extras
+
+Installing the devtools module also gives you access to the `dump` function from Symfony's VarDumper. You can read more about it [here](https://symfony.com/doc/current/components/var_dumper.html).
+
+```php
+dump($data);
+```
+
+## Deployment
+
+You should note that the devtools module is meant for use ONLY IN DEVELOPMENT. You should not use it in production as it can be a security risk. For now, the only way to disable the devtools is to remove the hook from your app or uninstall it completely.
+
+A small workaround is to add a condition to the hook so that it only runs in development mode. You can do this using the `script` method on the `Leaf\App` class.
+
+
diff --git a/apps/docs/src/modules/eien/http-server.md b/apps/docs/src/modules/eien/http-server.md
new file mode 100644
index 0000000..60badc2
--- /dev/null
+++ b/apps/docs/src/modules/eien/http-server.md
@@ -0,0 +1,135 @@
+# Http Server With Eien
+
+In the previous section, we saw how to get started with Eien. By default Leaf handles creating an Http server with Eien. However, you may want to build your own Eien server instead of just going with what Leaf gives you out of the box. This section will show you how to do just that.
+
+## Creating a Server
+
+Before we get started with creating the server, we need to make a little tweak to our Leaf app. We would need to remove the line which runs our Leaf app. This is because we want to run our app from our own server.
+
+
+
+```php
+// remove this line
+$app->run();
+```
+
+
+
+
+```php
+// remove this line
+app()->run();
+```
+
+
+
+Now all that we need to do is use the Eien server class to craft a beautiful server for our Leaf app.
+
+
+
+TO get started, you simply need to initialize the `Leaf\Eien\Server` class and pass in your Leaf app instance.
+
+```php
+$app = new Leaf\App();
+
+// your leaf routes
+
+$server = new Leaf\Eien\Server();
+$sever->wrap($app);
+```
+
+
+
+
+To get started, you simply need to call the `server` function and pass in your Leaf app instance.
+
+```php
+// your leaf routes
+server()->wrap(app());
+```
+
+
+
+After wrapping your app, you can then start your server by calling the `listen` method. This will tell Eien to listen for traffic, forward your requests and return a response.
+
+
+
+```php
+$server->listen();
+```
+
+
+
+
+```php
+server()->listen();
+```
+
+
+
+Putting it all together, your `index.php` file should look like this:
+
+
+
+That's how to build a simple Http server with eien.
+
+## Configuring Eien
+
+Since Eien uses Swoole under the hood, your configuration is mostly applied to Swoole. You can get started by passing in an array of configuration options to the `config` method.
+
+
diff --git a/apps/docs/src/modules/eien/index.md b/apps/docs/src/modules/eien/index.md
new file mode 100644
index 0000000..5c0122f
--- /dev/null
+++ b/apps/docs/src/modules/eien/index.md
@@ -0,0 +1,132 @@
+# Eien Server
+
+Eien is Leaf's implementation of a high-speed, high-performance server based on powerful tools like [Open Swoole](https://swoole.co.uk/) and [Swoole](https://github.com/swoole/swoole-src). Eien loads your app in memory and shares a state between requests to achieve amazing speeds.
+
+When serving Leaf apps with Eien, you gain additional performance improvements due to Leaf's lightweight and super fast nature.
+
+::: warning Note that
+Eien runs on Swoole, so you need to have the swoole extension installed.
+
+- [Swoole Installation docs](https://openswoole.com/docs/get-started/installation)
+- [In case you have errors installing swoole on Mac](https://parsinta.com/articles/setup-php-swoole-in-your-mac-os)
+:::
+
+## Installation
+
+You can easily install Eien using the [Leaf CLI](/docs/cli/):
+
+```bash
+leaf install eien
+```
+
+Or with [Composer](https://getcomposer.org/).
+
+```bash
+composer require leafs/eien
+```
+
+## Prerequisites
+
+Eien has been configured to rely on some features available in only v3 of Leaf. This means that you can't use Eien with earlier versions of the Leaf framework. Not to worry, if you're on Leaf 2, migrating to Leaf 3 should take less than 10 minutes. You can follow this [documentation to help you migrate](/docs/migration/introduction.html)
+
+## Benchmarks
+
+
+
+ Leaf WITHOUT Eien:
+
+
+
+ Leaf WITH Eien:
+
+
+
+
+***From the Benchmarks above, Leaf was 76x faster when used with Eien.***
+
+## Basic Usage
+
+Eien just like the rest of Leaf 3 was built with developer experience in mind. This means that for basic usage, which for most people will be speeding up their applications and simple http features, you don't need to make any modification to your application after installing Eien. Once Leaf detects Eien, it will automatically setup everything and get your app running faster than anything you've seen.
+
+::: warning Note
+Eien is still in it's dev phase, we have occasional releases, but Eien has not yet been fully tested in production and we'll need your help with that.
+:::
+
+## Serving Your Application
+
+You can start your application using the `php ` command. This command will start Eien which will then load your app. Since Eien loads your application to memory, any changes to your application's files will not be reflected when you refresh your browser so we don't recommend this method. You can think of it as starting your nodejs application with `node index.js`.
+
+What we really need in this case is to watch your files and restart them when there's any update. For this, we tweaked the `leaf serve` command from the Leaf CLI a bit. You can use the Leaf serve command to start Eien and watch for any changes.
+
+```bash
+leaf serve
+```
+
+## Drawbacks
+
+Since Eien is tied to Leaf, it means that you can't use it with other frameworks. This is because Leaf is built with a very specific architecture that makes it very fast and efficient. This means that you can't use Eien with other frameworks like Laravel, Symfony, etc.
+
+Another major thing to watch out for is the use of PHP functions for responses. All your headers and cookies need to pass through Leaf directly, otherwise Eien won't be able to handle them right. This means you can't use inbuilt PHP functions like `header()` or `setcookie()`. You'll need you use Leaf's `response->withHeader()` and `response->withCookie()` functions instead.
+
+## WebSockets
+
+> A WebSocket server is a network communication protocol which supports full-duplex communication over a TCP connection. A WebSocket server usually operations on traditional HTTP ports like 80 or 443, this makes it compatible with the HTTP protocol, but you can select other ports to run on. Compared with the HTTP protocol, which is stateless, a WebSocket Server can maintain a persistent connection, making it a stateful protocol. The connection between the client and server is kept alive.
+
+Eien now allows you to create routes that use websockets with Leaf. Just as with all of Leaf, there's no need for any configuration. You can just create a route and start using websockets.
+
+::: warning Note
+Since Eien's WebSocket functionality is still not fully production tested, it is only available through the beta channel. You'll need to install the beta versions of Leaf, Eien and Leaf Http to use websockets. You can do this by running the following commands:
+
+```bash
+leaf install leaf@3.2.2-beta eien@dev-main http@dev-main
+```
+
+:::
+
+To create a websocket route, simply use the `ws` method on the leaf app. Note that under the hood, only one websocket instance is created, however, you can create as many routes as you want. These routes will share the instance and will be smartly handled automatically by Eien.
+
+```php
+ws('/ws-route', function () {
+ response()->json([
+ 'message' => 'Hello from websocket'
+ ]);
+});
+
+app()->run();
+```
+
+As you can see, the websocket route is just like any other route. The only difference is that you use the `ws` method instead of the `get` or `post` method. You can use Leaf request, response, db and other modules just as you would in any other part of your app.
+
+The handler function for the websocket route is a callback function that will be called when a client sends a message to the websocket route. The callback function receives a `Swoole\WebSocket\Server` instance as it's first argument. It also receives a `Swoole\WebSocket\Frame` instance as it's second argument. The `Swoole\WebSocket\Server` instance is the websocket server instance and the `Swoole\WebSocket\Frame` instance is the message sent by the client.
+
+```php
+ws('/ws-route', function ($server, $frame) {
+ $server->push($frame->fd, 'Hello from websocket');
+});
+
+app()->run();
+```
+
+Of course, you can use Leaf's request and response modules to handle your websocket routes which is recommended over directly using the `Swoole\WebSocket\Server` instance.
+
+```php
+ws('/ws-route', function () {
+ response()->json([
+ 'message' => 'Hello from websocket'
+ ]);
+});
+
+app()->run();
+```
diff --git a/apps/docs/src/modules/experiments/index.md b/apps/docs/src/modules/experiments/index.md
new file mode 100755
index 0000000..aa295d1
--- /dev/null
+++ b/apps/docs/src/modules/experiments/index.md
@@ -0,0 +1,115 @@
+
+# 🍪 Cookies
+
+The Leaf application provides helper methods to send cookies with the HTTP response. From version 2.2 beta, the old `Leaf\Http\Cookies` package has been replaced by `Leaf\Http\Cookie`. This change also fixes the bug which prevented use of `Leaf\Http\Cookies` inside route handlers and controllers.
+
+## Init
+
+Unlike the former `Leaf\Http\Cookies` package, you can use `Leaf\Http\Cookie` methods without initialising the class:
+
+```php
+use Leaf\Http\Cookie;
+// ...
+Cookie::set("name", "Michael");
+```
+
+## Set
+
+This method replaces the previous `setCookie` method. It takes in 3 params:
+
+- cookie name (string|array)
+- cookie value (optional - string)
+- cookie options (optional - array)
+
+```php
+// normal method
+Cookie::set("name", "Michael");
+// using array
+Cookie::set(["name" => "Michael"]);
+```
+
+You can also set multiple cookies at a time
+
+```php
+Cookie::set([
+ "name" => "Michael",
+ "age" => "18"
+]);
+```
+
+Adding cookie options
+
+```php
+Cookie::set("name", "Michael", ["expire" => 0]);
+```
+
+Options for cookies are:
+
+- expire
+- path
+- domain
+- secure
+- httponly
+
+
+
+## simpleCookie
+
+This method allows you to quickly set a cookie and it's expiry time. It takes in 3 params:
+
+- cookie name (string|array)
+- cookie value (optional - string)
+- cookie expiresAt (optional - string - default of 7 days)
+
+```php
+Cookie::simpleCookie("name", "Michael", "2 days");
+```
+
+
+
+## all
+
+`all` returns all set cookies.
+
+```php
+$cookies = Cookie::all();
+```
+
+
+
+## get
+
+`get` returns a particular set cookie
+
+```php
+$name = Cookie::get("name");
+```
+
+
+
+## unset
+
+This method replaces the previous `deleteCookie` method. It takes in the cookie to unset.
+
+```php
+// normal method
+Cookie::unset("name");
+// using array
+Cookie::unset(["name"]);
+```
+
+You can also unset multiple cookies at a time
+
+```php
+Cookie::unset(["name", "age"]);
+```
+
+
+
+## unsetAll
+
+This method removes all set cookies.
+
+```php
+Cookie::unsetAll();
+```
diff --git a/apps/docs/src/modules/fetch/index.md b/apps/docs/src/modules/fetch/index.md
new file mode 100644
index 0000000..6231589
--- /dev/null
+++ b/apps/docs/src/modules/fetch/index.md
@@ -0,0 +1,352 @@
+# Leaf Fetch
+
+
+
+
+
+
+
+Fetch is a clean, simple, developer friendly interface for making network requests with PHP. It provides a modern API based on [axios](https://axios-http.com/docs/intro) and uses elements from [Unirest PHP](https://github.com/Kong/unirest-php).
+
+## Installation
+
+You can install leaf fetch leaf cli:
+
+```bash
+leaf install fetch
+```
+
+or with composer:
+
+```bash
+composer require leafs/fetch
+```
+
+## Usage
+
+Leaf fetch provides different ways of interacting with the fetch library. You can use the `Leaf\fetch` function or the `Leaf\Fetch` class. Leaf fetch also supports functional mode which means that you can use the global `fetch` method.
+
+## fetch example
+
+```php
+# import the fetch function from leaf
+use function Leaf\fetch;
+
+$res = fetch("https://jsonplaceholder.typicode.com/todos/");
+
+# data returned is saved in the $data property just like axios
+echo json_encode($res->data);
+```
+
+You can also use the fetch class
+
+```php
+use Leaf\Fetch;
+
+$res = Fetch::request([
+ "url" => 'https://jsonplaceholder.typicode.com/todos/1',
+]);
+
+echo json_encode($res->data);
+```
+
+## The `fetch` method
+
+Leaf fetch provides the fetch method as an easy way to make HTTP requests. This allows you to quickly make requests without bringing up the whole fetch class and without even having to build up your own request array.
+
+```php
+// make a get request
+$res = fetch("https://jsonplaceholder.typicode.com/todos/");
+
+// make a post request
+$res = fetch("https://jsonplaceholder.typicode.com/posts", [
+ "title" => "foo",
+ "body" => "bar",
+ "userId" => 1,
+]);
+
+// build a custom request array
+$res = fetch([
+ "method" => "GET",
+ "url" => 'https://jsonplaceholder.typicode.com/todos/1',
+ "data" => [
+ "firstName" => 'Fred',
+ "lastName" => 'Flintstone'
+ ]
+]);
+
+// get response body
+echo json_encode($res->data);
+```
+
+As shown in the example above, the fetch method tries to identify what type of request is being made based on the data that is being passed into it. For instance, simply passing a URL in without any parameters will make a `get` request.
+
+```php
+fetch("https://jsonplaceholder.typicode.com/todos/");
+
+# >> GET https://jsonplaceholder.typicode.com/todos/
+```
+
+If any parameters are specified, `fetch` will switch to a `POST` request.
+
+```php
+fetch("https://jsonplaceholder.typicode.com/posts", [
+ "title" => "foo",
+ "body" => "bar",
+ "userId" => 1,
+]);
+
+# >> POST https://jsonplaceholder.typicode.com/posts/
+# >> data -> title: foo, body: bar, userId: 1
+```
+
+If however, your type of request is not automatically determined by the `fetch` method, you can still manually tell fetch what to do by passing in an array of configuration data.
+
+```php
+fetch([
+ # HTTP method to send
+ "method" => "PUT",
+ # URL to hit
+ "url" => 'https://jsonplaceholder.typicode.com/todos/1',
+ # Request data to send along
+ "data" => [
+ "firstName" => 'Fred',
+ "lastName" => 'Flintstone'
+ ]
+]);
+```
+
+## The `Fetch` class
+
+The fetch class contains all the options and methods needed to make a network request. You can also use the `Fetch` class to configure how fetch handles requests. To get started, simply import the leaf fetch class.
+
+```php
+use Leaf\Fetch;
+```
+
+### baseUrl
+
+You might have noticed that all the requests above needed us to type a long URL to make the requests, however, we can add a base url so we don't have to type it over and over again.
+
+```php
+Fetch::baseUrl("https://jsonplaceholder.typicode.com");
+```
+
+And from there you can make requests like this:
+
+```php
+// make a get request
+$res = fetch("/todos");
+
+// make a post request
+$res = fetch("/posts", [
+ "title" => "foo",
+ "body" => "bar",
+ "userId" => 1,
+]);
+
+// use the get shortcut method
+$res = Fetch::get("/todos/10");
+
+// echo response
+echo json_encode($res);
+```
+
+As you noticed, configuration made on the fetch class also applies to the `fetch` method.
+
+### shortcut methods
+
+The fetch class comes with shortcut methods named after http methods `get`, `post`, `put`, `patch`, ...
+
+```php
+$res = Fetch::post("/posts", [
+ "title" => "foo",
+ "body" => "bar",
+ "userId" => 2,
+]);
+
+$res = Fetch::get("/todos/10");
+
+Fetch::delete("/todos/10");
+
+// ...
+```
+
+### request
+
+As you've seen earlier, the fetch class also provides a `request` method which is also used under the hood by the `fetch` method. `request` allows you to manually build up your request object with whatever data you need.
+
+```php
+use Leaf\Fetch;
+
+$res = Fetch::request([
+ "method" => "GET",
+ "url" => "https://jsonplaceholder.typicode.com/todos",
+]);
+
+echo json_encode($res->data);
+```
+
+### Request object
+
+This is the array which is used to construct the request to be sent. The available fields are:
+
+```php
+[
+ // `url` is the server URL that will be used for the request
+ "url" => null,
+
+ // `method` is the request method to be used when making the request
+ "method" => "GET", // default
+
+ // `baseURL` will be prepended to `url` unless `url` is absolute.
+ // It can be convenient to set `baseURL` for an instance of axios to pass relative URLs
+ // to methods of that instance.
+ "baseUrl" => "",
+
+ // `transformRequest` allows changes to the request data before it is sent to the server
+ // This is only applicable for request methods 'PUT', 'POST', 'PATCH' and 'DELETE'
+ // The last function in the array must return a string or an instance of Buffer, ArrayBuffer,
+ // FormData or Stream
+ // You may modify the headers object.
+ // "transformRequest" => function ($data, $headers) {
+ // // Do whatever you want to transform the data
+
+ // return $data;
+ // },
+
+ // `transformResponse` allows changes to the response data to be made before
+ // it is passed to then/catch
+ // "transformResponse" => function ($data) {
+ // // Do whatever you want to transform the data
+
+ // return $data;
+ // },
+
+ // `headers` are custom headers to be sent
+ "headers" => [],
+
+ // `params` are the URL parameters to be sent with the request
+ // Must be a plain object or a URLSearchParams object
+ "params" => [],
+
+ // `paramsSerializer` is an optional function in charge of serializing `params`
+ // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
+ // "paramsSerializer" => function ($params) {
+ // return Qs.stringify($params, ["arrayFormat" => "brackets"]);
+ // },
+
+ // `data` is the data to be sent as the request body
+ // Only applicable for request methods 'PUT', 'POST', 'DELETE , and 'PATCH'
+ // When no `transformRequest` is set, must be of one of the following types:
+ // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
+ // - Browser "only" => FormData, File, Blob
+ // - Node "only" => Stream, Buffer
+ "data" => [],
+
+ // `timeout` specifies the number of milliseconds before the request times out.
+ // If the request takes longer than `timeout`, the request will be aborted.
+ "timeout" => 0, // default is `0` (no timeout)
+
+ // `withCredentials` indicates whether or not cross-site Access-Control requests
+ // should be made using credentials
+ "withCredentials" => false, // default
+
+ // `adapter` allows custom handling of requests which makes testing easier.
+ // Return a promise and supply a valid response (see lib/adapters/README.md).
+ // "adapter" => function ($config) {
+ // /* ... */
+ // },
+
+ // `auth` indicates that HTTP Basic auth should be used, and supplies credentials.
+ // This will set an `Authorization` header, overwriting any existing
+ // `Authorization` custom headers you have set using `headers`.
+ // Please note that only HTTP Basic auth is configurable through this parameter.
+ // For Bearer tokens and such, use `Authorization` custom headers instead.
+ "auth" => [],
+
+ // `responseType` indicates the type of data that the server will respond with
+ // options "are" => 'arraybuffer', 'document', 'json', 'text', 'stream'
+ // browser "only" => 'blob'
+ "responseType" => "json", // default
+
+ // `responseEncoding` indicates encoding to use for decoding responses (Node.js only)
+ // "Note" => Ignored for `responseType` of 'stream' or client-side requests
+ "responseEncoding" => "utf8", // default
+
+ // `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
+ "xsrfCookieName" => "XSRF-TOKEN", // default
+
+ // `xsrfHeaderName` is the name of the http header that carries the xsrf token value
+ "xsrfHeaderName" => "X-XSRF-TOKEN", // default
+
+ // `onUploadProgress` allows handling of progress events for uploads
+ // browser only
+ // "onUploadProgress" => function ($progressEvent) {
+ // // Do whatever you want with the native progress event
+ // },
+
+ // `onDownloadProgress` allows handling of progress events for downloads
+ // browser only
+ // "onDownloadProgress" => function ($progressEvent) {
+ // // Do whatever you want with the native progress event
+ // },
+
+ // `maxContentLength` defines the max size of the http response content in bytes allowed in node.js
+ "maxContentLength" => 2000,
+
+ // `maxBodyLength` (Node only option) defines the max size of the http request content in bytes allowed
+ "maxBodyLength" => 2000,
+
+ // `validateStatus` defines whether to resolve or reject the promise for a given
+ // HTTP response status code. If `validateStatus` returns `true` (or is set to `null`
+ // or `undefined`), the promise will be resolved; otherwise, the promise will be
+ // rejected.
+ // "validateStatus" => function ($status) {
+ // return $status >= 200 && $status < 300; // default
+ // },
+
+ // `maxRedirects` defines the maximum number of redirects to follow in node.js.
+ // If set to 0, no redirects will be followed.
+ "maxRedirects" => 5, // default
+
+ // `socketPath` defines a UNIX Socket to be used in node.js.
+ // e.g. '/var/run/docker.sock' to send requests to the docker daemon.
+ // Only either `socketPath` or `proxy` can be specified.
+ // If both are specified, `socketPath` is used.
+ "socketPath" => null, // default
+
+ // `proxy` defines the hostname, port, and protocol of the proxy server.
+ // You can also define your proxy using the conventional `http_proxy` and
+ // `https_proxy` environment variables. If you are using environment variables
+ // for your proxy configuration, you can also define a `no_proxy` environment
+ // variable as a comma-separated list of domains that should not be proxied.
+ // Use `false` to disable proxies, ignoring environment variables.
+ // `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and
+ // supplies credentials.
+ // This will set an `Proxy-Authorization` header, overwriting any existing
+ // `Proxy-Authorization` custom headers you have set using `headers`.
+ // If the proxy server uses HTTPS, then you must set the protocol to `https`.
+ "proxy" => [],
+
+ // `decompress` indicates whether or not the response body should be decompressed
+ // automatically. If set to `true` will also remove the 'content-encoding' header
+ // from the responses objects of all decompressed responses
+ // - Node only (XHR cannot turn off decompression)
+ "decompress" => true, // default
+
+ // If false, fetch will try to parse json responses
+ "rawResponse" => false,
+
+ // CURLOPT_SSL_VERIFYHOST accepts only 0 (false) or 2 (true).
+ // Future versions of libcurl will treat values 1 and 2 as equals
+ "verifyHost" => true, // default
+
+ "verifyPeer" => true, // default
+
+ // Set additional options for curl.
+ "curl" => [],
+];
+```
+
+Built with ❤ by [**Mychi Darko**](https://mychi.netlify.app)
diff --git a/apps/docs/src/modules/forms/index.md b/apps/docs/src/modules/forms/index.md
new file mode 100755
index 0000000..8dedc1e
--- /dev/null
+++ b/apps/docs/src/modules/forms/index.md
@@ -0,0 +1,31 @@
+# Intro
+
+Leaf Form contains methods to simply and quickly handle input from the user.
+
+## Installation
+
+You can quickly install leaf forms using the leaf cli:
+
+```bash
+leaf install form
+```
+
+Or via composer:
+
+```bash
+composer require leafs/form
+```
+
+## Versions
+
+There are currently two versions of leaf forms. Click on your version of choice to view it's docs.
+
+- [version 2](/modules/forms/v/2/)
+- [version 1.2](/modules/forms/v/1.2/)
+- [version 1](/modules/forms/v/1/)
+
+**If you are not already using leaf forms, it is recommended that you use version 2.**
+
+## Upgrading
+
+You can quickly upgrade by running the installation command above.
diff --git a/apps/docs/src/modules/forms/v/1.2/index.md b/apps/docs/src/modules/forms/v/1.2/index.md
new file mode 100755
index 0000000..24873e6
--- /dev/null
+++ b/apps/docs/src/modules/forms/v/1.2/index.md
@@ -0,0 +1,497 @@
+---
+title: "Forms v1.2"
+---
+
+
+# Leaf Forms
+
+Leaf Form contains methods to simply and quickly handle input from the user.
+
+## Installation
+
+You can quickly install leaf forms using the following composer or the leaf cli.
+
+```bash
+leaf install form
+```
+
+or with composer
+
+```bash
+composer require leafs/form
+```
+
+
+
+## Functional Mode
+
+
+Leaf form now supports leaf 3's functional mode which allows you to simply call the `form` method from anywhere in your code. This returns an instance of the `Leaf\Form` class, so in case you can't use functional mode, simply call your method on `Leaf\Form`.
+
+```php
+form()->sanitizeInput(...);
+```
+
+
+
+
+## Form class
+
+Leaf form can be used by initializing the leaf form class.
+
+```php
+$form = new Leaf\Form;
+$form->sanitizeInput(...);
+```
+
+
+
+## sanitizeInput
+
+sanitizeInput offers basic security for input data, i.e. sanitizing input against SQL injection.
+
+
+
+If you however need better sanitizing, you can check out the [anchor module](/modules/anchor/)
+
+## Form Submit
+
+This creates a form and submits it. You can call it a virtual form. It takes in 3 parameters, the request type, the form action and the form data. Currently, it only supports GET and POST requests.
+
+
+
+## isEmail
+
+This checks if a field contains an email or not.
+
+
+
+```php
+if (form()->isEmail($field)) {
+ echo "This is an email";
+}
+```
+
+
+
+
+```php
+if ($form->isEmail($field)) {
+ echo "This is an email";
+}
+```
+
+
+
+## body
+
+This returns all fields passed into a request as an array.
+
+
+
+```php
+$data = form()->body();
+```
+
+
+
+
+```php
+$data = $form->body();
+```
+
+
+
+## Validation
+
+Validation is one of the most important features used in many different types of projects. Leaf Forms provides a very simple way to validate fields returned from forms, json data and even urls and files.
+
+### Validate
+
+Validate simply makes sure that the selected parameters pass these validation tests.
+
+Parameters which fail the form validation are saved in the form's errors which can be accessed with `errors()`. So In case the validation fails, `validate` returns false, else true.
+
+
+
+#### Inline validate
+
+
+For single rules, using an array takes up a few more lines and looks a bit clustered. For those cases, you can run your validation rules inline:
+
+
+
+### validateData
+
+validateData works pretty much the same way as `validate` except that instead of passing the name of the field you want to validate, you validate the data itself.
+
+
+
+### Multiple Rule Validation
+
+You can also pass an array as the rule parameter. If there's more than one rule, both of them will apply. Also, be sure not to use contradictory rules like `number` and `textOnly` or `username` and `email`.
+
+
+
+### Supported rules
+
+This is a list of all supported validate rules
+
+- `required`: field is required
+- `number`: must only contain numbers
+- `text` : must only contain text and spaces
+- `textOnly`: should be text **only**, no spaces allowed
+- `validUsername`: must only contain characters 0-9, A-Z and _
+- `username`: alias for validUsername
+- `email`: must be a valid email
+- `noSpaces`: can't contain any spaces
+- `max`: max length of a string (requires arguments)
+- `min`: min length of a string (requires arguments)
+- `date`: string should be a valid date
+
+
+**Note that these rules aren't case-sensitive, so you can type them anyway you prefer, as long as the spelling is the same.**
+
+### Custom Error Messages
+
+
+This has been one of the most sought after features in leaf form, and now it comes pre-packaged in this version. Using custom error messages, you can take your app a step further and define custom error messages in your language of choice. You can do this using the `messages` method.
+
+
+
+```php
+form()->messages('nospaces', '{field} no puede contener espacios');
+```
+
+
+
+
+```php
+$form->messages('nospaces', '{field} no puede contener espacios');
+```
+
+
+
+or
+
+
+
+```php
+form()->messages('min', '{field} doit comporter au moins 8 caractères');
+```
+
+
+
+
+```php
+$form->messages('min', '{field} doit comporter au moins 8 caractères');
+```
+
+
+
+Note the use of `{field}`. This is a mini template that tells leaf to replace {field} with the current field. So in this case:
+
+
+
+`{field}` will be replaced with `password`. Leaf form also supports `{value}` and `{params}` which are basically the value of the field being checked and parameters passed into the current rule if any.
+
+In the example above, `{value}` will be the value of `password` which the user passes into request, and `{params}` will be `8`: that's the parameter passed to the `min` rule.
+
+::: warning Note
+This only works for in-built rules, for custom rules, you can set your own error message using the `addError` method as done below.
+:::
+
+### Create your own rules
+
+Not every project is the same, as such, you might need validation rules which are not available by default in leaf. As such, the `rule` method has been created to give you leeway to write your own validation rules.
+
+*You will need to use `addError` to save error messages when the validation fails.*
+
+
+
+```php
+form()->rule("max", function ($field, $value, $params) {
+ if (strlen($value) > $params) {
+ form()->addError($field, "$field can't be more than $params characters");
+ return false;
+ }
+});
+```
+
+
+
+
+```php
+$form->rule("max", function ($field, $value, $params) use($form) {
+ if (strlen($value) > $params) {
+ $form->addError($field, "$field can't be more than $params characters");
+ return false;
+ }
+});
+```
+
+
+
+This is an example rule that makes sure that a string isn't longer than it should be. It takes in a `$params` value which is the max length of the string.
+
+To use this rule, you can simply call max and pass a value into the `$params` field using `:`.
+
+
+
+This example doesn't take in any parameters.
+
+### supportedRules
+
+You can also get all the supported rules. This includes custom rules you've created.
+
+
+
+### errors
+
+Remember we talked about Leaf Form errors? Leaf Form holds errors for all failed tests, you get all these errors back with `errors()`
+
+
diff --git a/apps/docs/src/modules/forms/v/1/index.md b/apps/docs/src/modules/forms/v/1/index.md
new file mode 100755
index 0000000..b9cc0d9
--- /dev/null
+++ b/apps/docs/src/modules/forms/v/1/index.md
@@ -0,0 +1,191 @@
+---
+title: "Forms v1"
+---
+
+
+# 🎢 Leaf Forms
+
+Leaf Form contains methods to simply and quickly handle input from the user.
+
+## Installation
+
+You can quickly install leaf forms using the following composer or the leaf cli.
+
+```bash
+composer require leafs/form v1.1
+```
+
+or
+
+```bash
+leaf install form@1.1
+```
+
+## sanitizeInput
+
+sanitizeInput offers basic security for input data, i.e. sanitizing input against SQL injection.
+
+```php
+$username = Form::sanitizeInput($username);
+```
+
+If you however need better sanitizing, you can check out the [anchor module](/modules/anchor/)
+
+## Form Submit
+
+This creates a form and submits it. You can call it a virtual form. It takes in 3 parameters, the request type, the form action and the form data. Currently, it only supports GET and POST requests.
+
+```php
+Form::submit("POST", "/book/create", [
+ "title" => "Book One",
+ "author" => "Mychi"
+]);
+```
+
+## isEmail
+
+This checks if a field contains an email or not.
+
+```php
+if (Form::isEmail($field)) {
+ echo "This is an email";
+}
+```
+
+## body
+
+This returns all fields passed into a request as an array.
+
+```php
+$data = Form::body();
+```
+
+## Validation
+
+Validation is one of the most important features used in many different types of projects. Leaf Forms provides a very simple way to validate fields returned from forms, json data and even urls and files.
+
+### Validate
+
+Validate simply makes sure that the selected parameters pass these validation tests.
+
+Parameters which fail the form validation are saved in the form's errors which can be accessed with `errors()`. So In case the validation fails, `validate` returns false, else true.
+
+```php
+$validatorSuccess = Form::validate([
+ "username" => "username",
+ "email" => "email",
+ "password" => "required"
+]);
+
+if (!$validatorSuccess) {
+ response()->exit(Form::errors());
+}
+```
+
+### validateData
+
+validateData works pretty much the same way as `validate` except that instead of passing the name of the field you want to validate, you validate the data itself.
+
+```php
+Form::validateData([
+ "mychi.darko" => "validUsername",
+ "mychi@leafphp.dev" => "email"
+]);
+```
+
+### validateField
+
+This method also allows you validate data, but compared, to the method above, this is much faster.
+
+```php
+Form::validateField("username", "michael", "validUsername");
+```
+
+### Multiple Rule Validation
+
+You can also pass an array as the rule parameter. If there's more than one rule, both of them will apply. Also, be sure not to use contradictory rules like `number` and `textOnly` or `username` and `email`.
+
+```php
+Form::validate([
+ "username" => "validUsername",
+ "email" => "email",
+ "password" => ["required", "noSpaces"]
+]);
+```
+
+### Supported rules
+
+This is a list of all supported validate rules
+
+- `required`: field is required
+- `number`: must only contain numbers
+- `text` : must only contain text and spaces
+- `textOnly`: should be text **only**, no spaces allowed
+- `validUsername`: must only contain characters 0-9, A-Z and _
+- `username`: alias for validUsername
+- `email`: must be a valid email
+- `noSpaces`: can't contain any spaces
+- `max`: max length of a string (require arguments)
+- `min`: min length of a string (require arguments)
+
+**Note that these rules aren't case-sensitive, so you can type them anyway you prefer, as long as the spelling is the same.**
+
+### Create your own rules
+
+Not every project is the same, as such, you might need validation rules which are not available by default in leaf. As such, the `rule` method has been created to give you leeway to write your own validation rules.
+
+*You will need to use `addError` to save error messages when the validation fails.*
+
+```php
+Form::rule("max", function ($field, $value, $params) {
+ if (strlen($value) > $params) {
+ Form::addError($field, "$field can't be more than $params characters");
+ return false;
+ }
+});
+```
+
+This is an example rule that makes sure that a string isn't longer than it should be. It takes in a `$params` value which is the max length of the string.
+
+To use this rule, you can simply call max and pass a value into the `$params` field using `:`.
+
+```php
+Form::validate([
+ "username" => "max:10",
+ ...
+```
+
+Adding the params field is not compulsory, you can create a rule that doesn't take a params field like this:
+
+```php
+Form::rule("required", function ($field, $value) {
+ if (($value == "" || $value == null)) {
+ Form::addError($field, "$field is required");
+ return false;
+ }
+});
+```
+
+This example doesn't take in any parameters.
+
+### supportedRules
+
+You can also get all the supported rules. This includes custom rules you've created.
+
+```php
+$formRules = Form::supportedRules();
+```
+
+### errors
+
+Remember we talked about Leaf Form errors? Leaf Form holds errors for all failed tests, you get all these errors back with `errors()`
+
+```php
+$validation = Form::validate([
+ "username" => "validUsername",
+ "email" => "email",
+ "password" => ["required", "noSpaces"]
+]);
+
+if (!$validation) return Form::errors();
+```
diff --git a/apps/docs/src/modules/forms/v/2/index.md b/apps/docs/src/modules/forms/v/2/index.md
new file mode 100644
index 0000000..2d32162
--- /dev/null
+++ b/apps/docs/src/modules/forms/v/2/index.md
@@ -0,0 +1,488 @@
+# Leaf Form
+
+During development, you often come across data that should meet some expectations. For example, a user's email address should be a valid email address. Or a user's password should be at least 8 characters long. Unfortunately, you can't always trust the data you deal with, especially when it comes from the user; that's why you need to validate it.
+
+Leaf provides a clean and simple interface to validate and use incoming data. We call this Leaf Form. It's not a form builder, it's not a form renderer, it's just a simple data validation library.
+
+## Installation
+
+You can quickly install Leaf Form using the [Leaf CLI](/docs/cli/):
+
+```bash
+leaf install form
+```
+
+Or via composer:
+
+```bash
+composer require leafs/form
+```
+
+## Validating data
+
+Leaf Form comes with a very handy `validate()` method that allows you to validate data. It takes in two arguments:
+
+- An array of the data to validate
+- The rules to validate the data against
+
+If the validation fails, the `validate()` method will return `false`. Otherwise, it will return the validated data.
+
+
+
+## Request Validation
+
+Leaf allows you to validate request data directly on the request object. Using this method, you will interface with Leaf's request object instead of Leaf form which means you can use it without manually installing Leaf Form. To get started, all you need to do is call `validate()` on the request object.
+
+
+
+Note that you don't need to pass in the data to validate. The request object will automatically get the data from the request.
+
+## Optional Fields
+
+By default, all fields are required. If you want to make a field optional, you can add the `optional` rule to the field. Once a field is optional, it will only be validated if it is present in the data and not null.
+
+
+
+## Available Rules
+
+Leaf Form comes with a number of built-in rules that you can use to validate data. Here's a list of all the available rules:
+
+| Rule | Description |
+| --- | --- |
+| `required` | The field under validation must be present in the input data and not empty. |
+| `email` | The field under validation must be formatted as an e-mail address. |
+| `text` | The field under validation must contain only alphabetic characters and spaces. |
+| `textOnly` | The field under validation must contain only alphabetic characters (no-spaces). |
+| `alpha` | The field under validation must contain only alphabetic characters. |
+| `alphaNum` | The field under validation must contain only alpha-numeric characters. |
+| `alphaDash` | The field under validation must contain only alpha-numeric characters, underscores, and dashes. |
+| `username` | The field under validation must contain only alpha-numeric characters and underscores. |
+| `number` | The field under validation must contain only numeric characters. |
+| `float` | The field under validation must contain only float values. |
+| `date` | The field under validation must be a valid date. |
+| `min` | The field under validation must have a minimum value. |
+| `max` | The field under validation must have a maximum value. |
+| `between` | The field under validation must be between two values in length. |
+| `match` | The field under validation must match a value. |
+| `contains` | The field under validation must contain a value. |
+| `in` | The field under validation must be included in a given list of values. |
+| `ip` | The field under validation must be a valid IP address. |
+| `ipv4` | The field under validation must be a valid IPv4 address. |
+| `ipv6` | The field under validation must be a valid IPv6 address. |
+| `url` | The field under validation must be a valid URL. |
+| `domain` | The field under validation must be a valid domain. |
+| `creditCard` | The field under validation must be a valid credit card number. |
+| `phone` | The field under validation must be a valid phone number. |
+| `uuid` | The field under validation must be a valid UUID. |
+| `slug` | The field under validation must be a valid slug. |
+| `json` | The field under validation must be a valid JSON string. |
+| `regex` | The field under validation must match a given regular expression. |
+
+## Passing Parameters To Rules
+
+Some rules like `min`, `max`, `between`, `match`, `contains`, `in` and `regex` require additional parameters. You can pass these parameters to the rules by separating them with a colon (`:`).
+
+
+
+## Array Validation
+
+Leaf provides easy ways to validate arrays of data. You can validate an array of data using the `array()` validator. You can also validate the items in an array using the dot notation.
+
+### `array()`
+
+The `array()` validator allows you to validate an array of data. This checks if the data is an array and if it contains the specified rules.
+
+
+
+The example above will check if the `users` field is an array. Let's say you want to check if the array contains only numeric values. You can do that by passing in the rules to the `array()` validator.
+
+
+
+**The `array()` method should not be used on arrays with key => value pairs.**
+
+### Dot Notation
+
+You can also validate the items in an array using the dot notation. This allows you to validate arrays with key => value pairs. For example, let's say you have an array holding a user with a name and an email address:
+
+```php
+$data = [
+ 'user' => [
+ 'name' => 'John Doe',
+ 'email' => 'john@example.com',
+ ],
+];
+```
+
+You can validate the user's name and email address like this:
+
+
+
+## Custom Rules
+
+You can create your own rules using the `addRule()` method or it's alias `rule()`. It takes in three arguments:
+
+- The name of the rule
+- The rule's handler
+- The rule's error message
+
+**Note:** The rule's handler must be either a regular expression or a callable that returns a boolean value.
+
+
+
+```php{1,3-8}
+Form::addRule('isEven', '/^\d*[02468]$/', 'The :attribute must be an even number.');
+
+Form::rule('superTest', function ($value) {
+ // in functions, you can also add the error messages like this
+ Form::message('superTest', '{field} should be superTest!');
+
+ return $value === 'superTest';
+});
+
+...
+
+
+Form::validate($data, [
+ 'age' => 'isEven',
+]);
+```
+
+
+
+
+```php{1,3-8}
+form()->rule('isEven', '/^\d*[02468]$/', 'The {field} must be an even number.');
+
+form()->rule('superTest', function ($value) {
+ // in functions, you can also add the error messages like this
+ form()->message('superTest', '{field} should be superTest!');
+
+ return $value === 'superTest';
+});
+
+...
+
+
+form()->validate($data, [
+ 'age' => 'isEven',
+]);
+```
+
+
+
+## Custom Error Messages
+
+You can customize the error messages for each rule by passing in an array of the rules and their error messages to the `message()` method. The keys of the array should be the names of the rules and the values should be the error messages.
+
+
+
+```php
+Form::message([
+ 'required' => '{field} is required',
+ 'email' => '{field} must be a valid email address',
+]);
+```
+
+
+
+
+```php
+form()->message([
+ 'required' => '{field} is required',
+ 'email' => '{field} must be a valid email address',
+]);
+```
+
+
+
+You can also customize the error messages for any rule by passing in the rule's name and the error message to the `message()` method.
+
+
+
+```php
+Form::message('required', '{field} is required');
+```
+
+
+
+
+```php
+form()->message('required', '{field} is required');
+```
+
+
+
+Note the use of `{field}`. This is a mini template that tells leaf to replace `{field}` with the current field. So in this case:
+
+
+
+The error message will be `name is required`. You can also use `{Field}` instead of `{field}` to get the field name with the first letter capitalized. You can also use `{value}` to get the value of the field.
diff --git a/apps/docs/src/modules/fs/index.md b/apps/docs/src/modules/fs/index.md
new file mode 100755
index 0000000..c1aab68
--- /dev/null
+++ b/apps/docs/src/modules/fs/index.md
@@ -0,0 +1,353 @@
+---
+title: "Leaf FileSystem"
+---
+
+
+# Leaf FS
+
+This is a simple functionality inspired by node js' FileSystem(fs) module. Leaf FS aims to make directory and file management much simpler than what you're currently used to, so as to speed up and ease the dev process.
+
+Leaf FS allows you to read/write to file, create, rename, copy and paste files/directories all with just a few lines of code. All this is performed while maintaining Leaf's simplicity.
+
+## Installation
+
+You can install leaf fs through composer:
+
+```bash
+composer require leafs/fs
+```
+
+or with leaf cli:
+
+```bash
+leaf install fs
+```
+
+## FS Directory Methods
+
+### createFolder
+
+This will create a new directory in the `current directory`
+
+Let's take this directory structure below, we initialise Leaf FS in our index.php file.
+
+```bash
+├───logs
+├───index.php
+```
+
+In our index.php file:
+
+```php
+FS::createFolder("new_logs");
+```
+
+After running this code, this is our new directory structure
+
+```bash
+├───logs
+├───new_logs
+├───index.php
+```
+
+### renameFolder
+
+renameFolder is used to rename a directory. It takes in two parameters: the directory you want to rename and it's new name/path.
+
+```php
+FS::renameFolder("new");
+FS::renameFolder("home/new", "home/items");
+```
+
+### deleteFolder
+
+deleteFolder is used to delete a directory. It takes in two parameters: the directory you want to delete.
+
+```php
+FS::deleteFolder("new", "items");
+FS::deleteFolder("home/new", "home/items");
+```
+
+### listDir
+
+listDir returns an array of all the files/folders in a directory. It takes in the directory to list and a search pattern eg: `*.php`. Also note that all folders end with a '/'
+
+```php
+FS::listDir("new");
+FS::listDir("home/new", "*.txt");
+```
+
+### listFolders
+
+This method lists all the folders in a particular directory.
+
+```php
+FS::listDir("folder");
+```
+
+## FS File Methods
+
+### createFile
+
+`createFile` is used to create a new file in the `current directory`. It takes in the filename or path+filename. If the file already exists, it gives the new file a different name.
+
+```php
+FS::createFile("items.txt");
+FS::createFile("home/items.txt");
+```
+
+### writeFile
+
+`writeFile` is used to add content to a file, if the file already has content, all the content in there is replaced with the new content. Also, if the file doesn't exist, it will be created and all the content will be added to it. It takes in 2 parameters, the name/path+name of the file and the content;
+
+```php
+FS::writeFile("items.txt", "Hello");
+FS::writeFile("items.txt", [
+ "name" => "Item 1"
+]);
+FS::writeFile("items.txt", 1);
+```
+
+### append
+
+append is almost exactly the same as writeFile, except that instead of replacing the content in a file, it adds to the end of it.
+
+```php
+$data = FS::append("items.txt", "Item name");
+```
+
+### prepend
+
+prepend is almost exactly the same as writeFile, except that instead of replacing the content in a file, it adds to the begining.
+
+```php
+$data = FS::prepend("items.txt", "Item name");
+```
+
+### readFile
+
+readFile returns the data found in a file. It takes 1 parameter: the file name/path+file name.
+
+```php
+$data = FS::readFile("./home/items.txt");
+```
+
+### renameFile
+
+renameFile renames a file. It takes 2 parameter: the file name/path+file name to rename and it's new name.
+
+```php
+$data = FS::renameFile("./home/items.txt", "home/products.txt");
+```
+
+### deleteFile
+
+deleteFile deletes a file. It takes 2 parameter: the file name/path+file name to delete.
+
+```php
+$data = FS::deleteFile("./home/items.txt");
+```
+
+### copyFile
+
+copyFile copies a file from the current directory to another directory. It takes in 3 parameters: the filename, the new path + filename and whether to rename the file if it exists in the new directory. The 3rd parameter is optional, if nothing is passed for the 3rd parameter, it will rename the file. Pass in true to rename the file if it already exists, false to override the file content(default is true).
+
+```php
+$data = FS::copyFile("items.txt", "./home/");
+$data = FS::copyFile("items.txt", "./home/", false);
+```
+
+### deepCopy
+
+Recursively copy through a folder.
+
+```php
+FS::deepCopy("source", "destination");
+```
+
+### superCopy
+
+Copy a file, or recursively copy a folder and its contents. Based on [Aidan Lister's copyr](http://aidanlister.com/2004/04/recursively-copying-directories-in-php/)
+
+```php
+$permissions = 0755;
+FS::superCopy("source", "destination", $permissions);
+```
+
+### cloneFile
+
+cloneFile also copies a file from the current directory to another directory, but unlike copyFile, cloneFile includes the filename, and it takes in 2 parameters: the filename and the path+filename to clone to.
+
+```php
+$data = FS::cloneFile("items.txt", "./home/products.txt");
+```
+
+### moveFile
+
+moveFile also moves a file from the current directory to another directory, it takes in 2 parameters: the filename and the path to move to.
+
+```php
+$data = FS::moveFile("items.txt", "./home/");
+```
+
+### allFiles
+
+This method allows you to get all of the files from the given directory (recursive). It takes in the directory to search and whether to show hidden files or not.
+
+```php
+$files = FS::allFiles("records", false);
+```
+
+### uploadFile
+
+
+This method was previously upload. In v2.4, uploadFile has received a lot of fixes and new features. Also the older upload method has been removed.
+
+
+`uploadFile` as the name suggests is a method that makes file uploading a breeze. This is the main highlight of `Leaf\FS` in v2.4. Also unlike in earlier versions, `uploadFile` supports more type of files.
+
+It takes in 3 parameters:
+
+- The file to upload
+- The path to save the file
+- Config for file upload
+
+```php
+$profilePic = $request->files("profile_pic");
+
+// file upload
+Leaf\FS::uploadFile($profilePic, "./images/");
+```
+
+One amazing thing about FS is that it can detect the type of file you're trying to upload and handle it accordingly, so you don't need to worry about that. Below is a table of common file types which are automatically detected.
+
+| File Type | Common Extensions |
+| :-------------- | :---------------------------------------------- |
+| image | 'jpg', 'jpeg', 'png', 'gif', 'webp', 'apng', 'tif', 'tiff', 'svg', 'pjpeg', 'pjp', 'jfif', 'cur', 'ico' |
+| video | 'mp4', 'webm', 'swf', 'flv' |
+| audio |'wav', 'mp3', 'ogg', 'm4a' |
+| text |'txt', 'log', 'xml', 'doc', 'docx', 'odt', 'wpd', 'rtf', 'tex', 'pdf' |
+| presentation |'ppsx', 'pptx', 'ppt', 'pps', 'ppsm', 'key', 'odp' |
+| compressed |'zip', 'rar', 'bz', 'gz', 'iso', 'tar.gz', 'tgz', 'zipx', '7z', 'dmg'|
+| spreadsheet |'ods', 'xls', 'xlsx', 'xlsm' |
+| application |'apk', 'bat', 'cgi', 'pl', 'com', 'exe', 'gadget', 'jar', 'msi', 'py', 'wsf' |
+
+You can open an issue if you think there should be a new category or if an important extension is not present here.
+
+**Upload Config**:
+
+You can configure file uploads to behave the way you want it to, and that's the 3rd parameter it takes in.
+
+```php
+Leaf\FS::uploadFile($profilePic, "./images/", []);
+```
+
+Config is an array that takes in particular properties:
+
+```php
+Leaf\FS::uploadFile($profilePic, "./images/", [
+ "verify_dir" => true
+]);
+```
+
+This is a list of config options.
+
+| Config Name | Description | Possible Values |
+| :-------------- | :---------------------------------------------- | -----------------------: |
+| unique | If `true`, a timestamp is added to filename | `true`, `false` |
+| verify_dir | Add error if upload directory is invalid | `true`, `false` |
+| verify_file | Add error if same filename exists | `true`, `false` |
+| max_file_size | Set maximum file size in bytes, default: 10mb | integer |
+| file_type | Set file type if it's not included in default list | string |
+| validate | Validate file type for invalid extensions: Only works with the default extensions | `true`, `false` |
+
+If the file upload is successful, it returns the filename, you can perform whatever operation you need on the filename, if it fails you can access the errors through the `errors` method. Refer to error handling for this part.
+
+```php
+$filename = FS::uploadFile($profilePic, "./images/", []);
+```
+
+### uploadInfo
+
+When the file successfully uploads, records on the file details are shelved. You can access these with the `uploadInfo` method. It takes in one optional parameter, the name of the file whose info you want to return.
+
+```php
+// returns info on all uploaded files (associative array)
+$uploadInfo = Leaf\FS::uploadInfo();
+
+// returns info on only selected file
+$profileInfo = FS::uploadInfo($filename);
+```
+
+### chmod
+
+Get or set UNIX mode of a file or directory.
+
+```php
+$mode = FS::chmod("items.txt");
+FS::chmod("items.txt", ...);
+```
+
+### link
+
+Create a symlink to the target file or directory. On Windows, a hard link is created if the target is a file.
+
+```php
+FS::link("items.txt", "items");
+```
+
+### name
+
+Extract the file name from a file path.
+
+```php
+$name = FS::name("works/log.txt");
+```
+
+### basename
+
+Extract the trailing name component from a file path.
+
+```php
+$name = FS::basename("works/log.txt");
+```
+
+### dirname
+
+Extract the parent directory from a file path.
+
+```php
+$dirname = FS::dirname("works/log.txt");
+```
+
+### extension
+
+Extract the file extension from a file path.
+
+```php
+$extension = FS::extension("works/log.txt");
+```
+
+### type
+
+Get the file type of a given file.
+
+```php
+$type = FS::type("works/log.txt");
+```
+
+### size
+
+Get the file size of a given file.
+
+```php
+$size = FS::size("works/log.txt");
+```
+
+## errors
+
+Just like most leaf modules, fs also has an errors method which returns any errors that fs might have run into during an operation.
+
+```php
+$errors = FS::errors();
+```
diff --git a/apps/docs/src/modules/http/index.md b/apps/docs/src/modules/http/index.md
new file mode 100644
index 0000000..3a5df74
--- /dev/null
+++ b/apps/docs/src/modules/http/index.md
@@ -0,0 +1,74 @@
+---
+title: "Introduction"
+---
+
+# Http Module
+
+::: tip 🎊 Http v2 released
+We just released Leaf Http v2. It comes with a ton of bug fixes and updated functionality for almost each class. These changes include standardization of some practices and performance upgrades.
+:::
+
+The Leaf Http module contains a bunch of handlers for managing the kinds and methods through which data flows in and out of your application.
+
+
+
+The available classes in the Http module are:
+
+
+
+- [`Leaf\Http\Request`](/modules/http/v/2/request)
+- [`Leaf\Http\Response`](/modules/http/v/2/response)
+- [`Leaf\Http\Headers`](/modules/http/v/2/headers)
+- [`Leaf\Http\Cache`](/modules/http/v/2/cache)
+- [`Leaf\Http\Status`](/modules/http/v/2/status)
+
+## Installation
+
+You can install the http module with the [Leaf CLI](/docs/cli/):
+
+```bash
+leaf install http
+```
+
+or with Composer:
+
+```bash
+composer require leafs/http
+```
+
+From there you can use any of the classes above in your project.
+
+::: tip
+Cookies and session are independent modules which are not added to the Http module. This is because, the use of session and cookies is relatively low in APIs. If you however want to use sessions and cookies, you can read their guides for information on them.
+:::
+
+## Versions
+
+There are currently two versions of Leaf Http. Click on your version of choice to view its docs.
+
+- [v1](/modules/http/v/1/)
+- [v2](/modules/http/v/2/)
+
+## Request
+
+This is a developer friendly interface which allows you to interact with data coming into your application. [Read the docs](/modules/http/v/2/request)
+
+## Response
+
+This interface allows you to output data from your application in different forms. [Read the docs](/modules/http/v/2/response)
+
+## Headers
+
+This interface allows you to manage headers in your application. [Read the docs](/modules/http/v/2/headers)
+
+## Cache
+
+This interface allows you to manage HTTP cache in your app. [Read the docs](/modules/http/v/2/cache)
+
+## Session (module)
+
+This module allows you to manage session in your application. [Read the docs](/modules/session/)
+
+## Cookies (module)
+
+This module allows you to manage cookies in your application. [Read the docs](/modules/cookies/)
diff --git a/apps/docs/src/modules/http/v/1/cache.md b/apps/docs/src/modules/http/v/1/cache.md
new file mode 100755
index 0000000..4f0c8d7
--- /dev/null
+++ b/apps/docs/src/modules/http/v/1/cache.md
@@ -0,0 +1,68 @@
+# Http Cache
+
+
+
+::: warning Watch out
+Leaf http cache is a class available on the leaf http module. Check out the [http module docs](/modules/http/) for installation instructions.
+:::
+
+Http Cache is a new class added on the http module that allows you to perform some basic caching on leaf response handlers. After installing the http module, you can use all caching methods.
+
+## Usage
+
+You can use caching via the `etag`, `lastModified`, and `expires` methods. It is best to use one of `etag` or `lastModified` - in conjunction with `expires` - per route; never use both `etag` and `lastModified` together in the same route callback.
+
+The `etag` and `lastModified` methods should be invoked in a route callback before other code; this allows Leaf to check conditional GET requests before processing the route callback’s remaining code.
+
+Both `etag` and `lastModified` instruct the HTTP client to store the resource response in a client-side cache. The `expires` method indicates to the HTTP client when the client-side cache should be considered stale.
+
+### etag
+
+An ETag is a unique identifier for a resource URI. After setting the Etag headers, the HTTP client will send an `If-None-Match` header with each subsequent HTTP request of the same resource URI. If the ETag value for the resource URI matches the `If-None-Match` HTTP request header, GET and HEAD requests will return a `304 Not Modified` HTTP response while all others return a `421 Precondition Failed` that will prompt the HTTP client to continue using its cache; this also prevents Leaf from serving the entire markup for the resource URI, saving bandwidth and response time.
+
+Setting an ETag with Leaf is very simple. Invoke Leaf’s etag method in your route callback, passing it a unique ID as the first and only argument.
+
+```php
+use \Leaf\Http\Headers;
+
+$app->get("/", function () use ($app) {
+ Headers::etag("unique-tag");
+
+ echo "This will be cached after the initial request!";
+});
+```
+
+That’s it. Make sure the ETag ID is unique for the given resource. Also make sure the ETag ID changes as your resource changes; otherwise, the HTTP client will continue serving its outdated cache.
+
+### expires
+
+Used in conjunction with the Leaf application’s etag or lastModified methods, the expires method sets an Expires header on the HTTP response informing the HTTP client when its client-side cache for the current resource should be considered stale. The HTTP client will continue serving from its client-side cache until the expiration date is reached, at which time the HTTP client will send a conditional GET request to the Leaf application.
+
+The expires method accepts one argument: an integer UNIX timestamp, or a string to be parsed with strtotime.
+
+```php
+use \Leaf\Http\Headers;
+
+$app->get("/", function () use ($app) {
+ Headers::etag("unique-tag");
+ Headers::expires("+1 week");
+
+ echo "This will be cached client-side for one week";
+});
+```
+
+### lastModified
+
+A Leaf provides built-in support for HTTP caching using the resource’s last modified date. When you specify a last modified date, Leaf tells the HTTP client the date and time the current resource was last modified. The HTTP client will then send a If-Modified-Since header with each subsequent HTTP request for the given resource URI. If the last modification date you specify matches the If-Modified-Since HTTP request header, the Leaf will return a 304 Not Modified HTTP response that will prompt the HTTP client to use its cache; this also prevents the Leaf from serving the entire markup for the resource URI saving bandwidth and response time.
+
+Setting a last modified date with Leaf is very simple. You only need to invoke the Leaf’s lastModified() method in your route callback passing in a UNIX timestamp of the last modification date for the given resource. Be sure the lastModified() method’s timestamp updates along with the resource’s last modification date; otherwise, the browser client will continue serving its outdated cache.
+
+```php
+use \Leaf\Http\Headers;
+
+$app->get("/", function () use ($app) {
+ Headers::lastModified(1617383991);
+
+ echo "This will be cached after the initial request!";
+});
+```
diff --git a/apps/docs/src/modules/http/v/1/headers.md b/apps/docs/src/modules/http/v/1/headers.md
new file mode 100755
index 0000000..dbb0cdf
--- /dev/null
+++ b/apps/docs/src/modules/http/v/1/headers.md
@@ -0,0 +1,187 @@
+# Leaf Headers
+
+
+::: warning Watch out
+Leaf request is a class available on the leaf http module. Check out the [http module docs](/modules/http/) for installation instructions.
+:::
+
+In previous versions of Leaf, Headers have been added to the request and response objects and could not be fully accesed directly, however, v2.4 provides a Headers object which allows you perform all header operations smoothly. Another amazing thing is that all Leaf Header methods are static, and so can be called directly without initializing the Headers object.
+
+
+You can still use most header methods from within the response and request objects, you can refer to those if you want to, however, this package comes with ore features and better useability.
+
+
+To get started with the Headers object, you simply need to call whatever method you need on the `Leaf\Http\Headers` object. Since it's static, there's no need to initialize it.
+
+## Headers Methods
+
+Below are the methods you can use on the Headers object:
+
+### status
+
+This method sets or returns the base HTTP status of a response. Response methods allow you to directly set http status codes, however, if you want to use PHP's native output methods, you can set the status code here.
+
+```php
+// ...
+Leaf\Http\Headers::status(404);
+echo "Page not found";
+```
+
+You can also return the currently set status code.
+
+```php
+$code = Leaf\Http\Headers::status();
+```
+
+
+
+### resetStatus
+
+If for some reason, you're not able to set the status using `status`, you can always fallback to `resetStatus`. This method uses PHP's inbuilt `http_response_code`.
+
+```php
+// ...
+Leaf\Http\Headers::resetStatus(200);
+echo 'Something here';
+```
+
+
+
+### all
+
+This method returns all headers passed into your Leaf app. It takes in a single optional parameter, whether to sanitize header data or not, it is set to false by default.
+
+```php
+// will not sanitize headers
+Leaf\Http\Headers::all();
+
+// will not sanitize headers
+Leaf\Http\Headers::all(false);
+
+// will sanitize headers
+Leaf\Http\Headers::all(true);
+```
+
+
+
+### get
+
+This method as the name implies returns a particular header.
+
+```php
+$content = Leaf\Http\Headers::get("Content-Type");
+```
+
+You can also get multiple headers at the same time.
+
+```php
+$headerGroup = Leaf\Http\Headers::get(["Content-Type", "Authorization"]);
+```
+
+Just like `all`, you can also sanitize the information from `get`.
+
+```php
+$data = Leaf\Http\Headers::get("header", true);
+```
+
+
+
+### set
+
+`set` allows you to set a new response header. It takes in 4 parameters:
+
+- The header to to set
+- Value for header
+- Replace similar header?
+- An http status code
+
+```php
+Leaf\Http\Headers::set("location", "/home", true, 302);
+```
+
+You can also set multiple values at once.
+
+```php
+Leaf\Http\Headers::set(["location" => "/home", "something" => "here"]);
+```
+
+If you want multiple headers with the same name, you can set replace to `false`. This will force multiple headers of the same type.
+
+```php
+Leaf\Http\Headers::set([
+ "WWW-Authenticate" => "Negotiate",
+ "WWW-Authenticate" => "NTLM"
+], null, false);
+```
+
+
+
+### remove
+
+This method removes previously set headers.
+
+```php
+// single value
+Leaf\Http\Headers::remove("WWW-Authenticate");
+
+// multiple value
+Leaf\Http\Headers::remove(["Content-Type", "WWW-Authenticate"]);
+```
+
+## Utility Header methods
+
+Some shortcut methods have been prepared for the most used headers, so you won't need to stress yourself writing a bunch of stuff for simple tasks.
+
+### contentPlain
+
+This method set's the content type of the response to `text/plain`, it also takes in an HTTP status code.
+
+```php
+Leaf\Http\Headers::contentPlain(200);
+echo "plain text here";
+```
+
+### contentHtml
+
+This method set's the content type of the response to `text/html`, it also takes in an HTTP status code.
+
+```php
+Leaf\Http\Headers::contentHtml(200);
+echo "html here";
+```
+
+### contentXml
+
+This method set's the content type of the response to `application/xml`, it also takes in an HTTP status code.
+
+```php
+Leaf\Http\Headers::contentXml(200);
+echo "Xml here";
+```
+
+### contentJSON
+
+This method set's the content type of the response to `application/json`, it also takes in an HTTP status code.
+
+```php
+Leaf\Http\Headers::contentJSON(200);
+echo "json here";
+```
+
+### accessControl
+
+This method allows you to quickly set `Access-Control` headers in your app. It takes in 3 parameters:
+
+- The header to set
+- The value to set
+- A status code (optional)
+
+```php
+Leaf\Http\Headers::accessControl("Allow-Origin", "https://example.com", 200);
+```
+
+You can set mutiple access control headers at once:
+
+```php
+Leaf\Http\Headers::accessControl(["Allow-Origin" => "*", "Allow-Headers" => "*"]);
+```
diff --git a/apps/docs/src/modules/http/v/1/index.md b/apps/docs/src/modules/http/v/1/index.md
new file mode 100644
index 0000000..e4d3e45
--- /dev/null
+++ b/apps/docs/src/modules/http/v/1/index.md
@@ -0,0 +1,69 @@
+---
+title: "Introduction"
+aside: none
+---
+
+# Leaf Http v1
+
+::: warning
+There is no need to manually add the Http module if you're using Leaf 3 since this is done for you automatically.
+:::
+
+::: tip 🎊 Http v2 released
+We just released Leaf Http v2. It comes with a ton of bug fixes and updated functionality for almost each class. These changes include standardization of some practices and performance upgrades.
+
+[Read the docs](/modules/http/v/2/)
+:::
+
+The Leaf Http module contains a bunch of handlers for managing the kinds and methods through which data flows in and out of your application.
+
+The available classes in the Http module are:
+
+- [`Leaf\Http\Request`](/modules/http/v/1/request)
+- [`Leaf\Http\Response`](/modules/http/v/1/response)
+- [`Leaf\Http\Headers`](/modules/http/v/1/headers)
+- [`Leaf\Http\Cache`](/modules/http/v/1/cache)
+
+## Installation
+
+You can install the http module with the [leaf cli](/docs/cli/):
+
+```bash
+leaf install http
+```
+
+or with composer:
+
+```bash
+composer require leafs/http
+```
+
+From there you can use any of the classes above in your project.
+
+::: tip
+Cookies and session are independent modules which are not added to the Http module. This is because, the use of session and cookies is relatively low in APIs. If you however want to use sessions and cookies, you can read their guides for information on them.
+:::
+
+## Request
+
+This is a developer friendly interface which allows you to interact with data coming into your application. [Read the docs](/modules/http/v/1/request)
+
+## Response
+
+This interface allows you to output data from your application in different forms. [Read the docs](/modules/http/v/1/response)
+
+## Headers
+
+This interface allows you to manage headers in your application. [Read the docs](/modules/http/v/1/headers)
+
+## Cache
+
+This interface allows you to manage http cache in your app. [Read the docs](/modules/http/v/1/cache)
+
+## Session (module)
+
+This module allows you to manage session in your application. [Read the docs](/modules/session/)
+
+## Cookies (module)
+
+This module allows you to manage cookies in your application. [Read the docs](/modules/cookies/)
diff --git a/apps/docs/src/modules/http/v/1/request.md b/apps/docs/src/modules/http/v/1/request.md
new file mode 100755
index 0000000..a1d9f19
--- /dev/null
+++ b/apps/docs/src/modules/http/v/1/request.md
@@ -0,0 +1,436 @@
+# Leaf Request
+
+
+::: warning Watch out
+Leaf request is a class available on the leaf http module. Check out the [http module docs](/modules/http/) for installation instructions.
+:::
+
+The request object is an abstraction of the current HTTP request and allows you to easily interact with any data passed into your application.
+
+## Request class
+
+The request class allows you to quickly access all the features of leaf request.
+
+```php
+Leaf\Http\Request::get("name");
+
+// or
+
+use Leaf\Http\Request;
+
+Request::get("name");
+```
+
+## Request on the Leaf Instance
+
+If you are using request in a leaf app, leaf automatically binds the request class to the leaf instance, so you can always access the leaf request object without having to include any classes or namespaces.
+
+```php{4}
+$app = new Leaf\App;
+
+$app->post("/user/change-username", function () use($app) {
+ echo $app->request()->get("username");
+});
+```
+
+## Functional Mode
+
+
+Request now hooks into leaf 3's functional mode and comes with global functions you can use anywhere in your app. Read the [functional mode docs](/docs/tooling/functions) for all the information on functional mode.
+
+```php{2}
+app()->post("/items/add", function () {
+ echo request()->get("username");
+});
+```
+
+As you noticed above, we simply call the `request` method without doing anything. Everything is taken care of for us. Also, now, you can use this feature even when you are not using Leaf in your app.
+
+You can also pass an array or string to the `request` method to directly retrieve information from the request body.
+
+```php
+$name = request("name");
+```
+
+## Basic Usage
+
+### get()
+
+`get()` is a general purpose method which retrieves a particular item from the request body. In simpler terms, it works like `$_POST['key']` but works for all request types. It takes in one parameter: the key of the parameter you wish to get.
+
+```php
+$app->post('/name/add', function () use($app) {
+ $name = $app->request()->get('name');
+});
+
+// get: linkToApp?id=1
+$id = $app->request()->get('id');
+```
+
+In v2.4, `get` can also be used on files passed into the request.
+
+```php
+$picture = $request->get("image");
+```
+
+### Multiple select
+
+In v2.4, you can retrieve a couple of fields you want, and not just one. You can also use this as a filter to return only the data you want in your app instead of using `body` which dumps all request data.
+
+```php
+$loginData = $request->get(["username", "password"]);
+// ... do something with username
+echo $loginData["username"];
+```
+
+This allows you to set data you need dynamically.
+
+```php
+list($username, $password) = array_values($request->get(["username", "password"]));
+// ... do something with username
+echo $username;
+```
+
+### Security Fixes
+
+`get()` has also received a bunch of security fixes which prevent maliscious scripts from being passed into your application. In v2.4, you can choose not to sanitize data coming into your application by passing in `false` as the second parameter.
+
+```php
+// data is sanitized
+$username = $request->get("username");
+// data is sanitized
+$title = $request->get("title", true);
+// data is not sanitized
+$blog = $request->get("blogBody", false);
+```
+
+
+
+## try()
+
+`try()` works just like `get` above, except that it conditionally returns items in the request. Let's look at an example:
+
+```php
+// get request: linkToApp?name=mychi
+$data = $app->request()->try(["name", "email"]);
+
+// $data -> ["name" => "mychi"];
+```
+
+Unlike `get` and `body`, if the parameter to find in the request is not found, it will automatically be removed from the data returned. You can also remove empty strings from the request by passing `true` as a third parameter.
+
+The available parameters are:
+
+- array - The parameters to return
+- bool - Sanitize output? Default `true`
+- bool - Remove empty strings from return data? Default `false`
+
+### Multiple select
+
+In v2.4, you can retrieve a couple of fields you want, and not just one. You can also use this as a filter to return only the data you want in your app instead of using `body` which dumps all request data.
+
+```php
+$loginData = $request->get(["username", "password"]);
+// ... do something with username
+echo $loginData["username"];
+```
+
+This allows you to set data you need dynamically.
+
+```php
+list($username, $password) = array_values($request->get(["username", "password"]));
+// ... do something with username
+echo $username;
+```
+
+
+
+## body()
+
+`body()` is another general purpose method which retrieves the key => value pairs of the entire request body. In simpler terms, it works like `$_POST` but works for all request types. In v2.4, `body` can also retrieve files passed into the request.
+
+```php
+$app->post('/name/add', function () use($app) {
+ $body = $app->request()->body();
+});
+```
+
+### 🧐 Security Fixes
+
+`body()` has also received a bunch of security fixes which prevent maliscious scripts from being passed into your application. Just like with `get`, v2.4 you the option to not sanitize data coming into your application by passing in `false`.
+
+```php
+// data is sanitized
+$body = $app->request()->body();
+// data is sanitized
+$body = $app->request()->body(true);
+// data is not sanitized
+$body = $app->request()->body(false);
+```
+
+
+
+## files
+
+Files returns an array holding key values pairs of files passed into your app.
+
+```php
+$image = $request->files("profile_pic");
+```
+
+You can also get multiple files
+
+```php
+list($profile, $avatar) = array_values($request->files(["profile", "avatar"]));
+```
+
+
+
+## Cookies
+
+Leaf also provides a simple `cookies` method on the request object which allows you to get cookie data.
+
+```php
+// get specific cookie
+$request->cookies("name");
+// get all cookies
+$request->cookies();
+```
+
+
+
+## headers
+
+A Leaf application will automatically parse all HTTP request headers. You can access the request headers using the request object’s `headers` method.
+
+```php
+request()->headers();
+
+// Get the ACCEPT_CHARSET header
+$charset = $app->request()->headers('ACCEPT_CHARSET');
+```
+
+The HTTP specification states that HTTP header names may be uppercase, lowercase, or mixed-case. Leaf is smart enough to parse and return header values whether you request a header value using upper, lower, or mixed case header name, with either underscores or dashes. So use the naming convention with which you are most comfortable.
+
+
+
+## Request Method
+
+Every HTTP request has a method (e.g. GET or POST). You can obtain the current HTTP request method via the Leaf application’s request object:
+
+### typeIs
+
+In v2.4, individual request methodtype checks have been replaced with `typeIs`. So you'll no longer be seeing any of these:
+
+```php
+$app->request()->isGet();
+$app->request()->isPost();
+$app->request()->isPut();
+$app->request()->isDelete();
+$app->request()->isHead();
+$app->request()->isOptions();
+$app->request()->isPatch();
+```
+
+instead, you'll be working with:
+
+```php
+$isGetRequest = $app->request()->typeIs("GET");
+$isPostRequest = $app->request()->typeIs("post");
+$isDeleteRequest = $app->request()->typeIs("Delete");
+
+if ($isGetRequest) $response->throwErr("GET method not allowed");
+```
+
+Here are some other functions you can use relating to the request method.
+
+```php
+/**
+ * What is the request method?
+ * @return string (e.g. GET, POST, PUT, DELETE)
+ */
+$app->request()->getMethod();
+
+/**
+ * Is this a XHR/AJAX request?
+ * @return bool
+ */
+$app->request()->isAjax();
+```
+
+
+
+## XHR
+
+When using a Javascript framework like MooTools or jQuery to execute an XMLHttpRequest, the XMLHttpRequest will usually be sent with a **X-Requested-With** HTTP header. The Leaf application will detect the HTTP request’s **X-Requested-With** header and flag the request as such. If for some reason an XMLHttpRequest cannot be sent with the **X-Requested-With** HTTP header, you can force the Leaf application to assume an HTTP request is an XMLHttpRequest by setting a GET, POST, or PUT parameter in the HTTP request named “isajax” with a truthy value.
+
+Use the request object’s `isAjax()` or `isXhr()` method to tell if the current request is an XHR/Ajax request:
+
+```php
+$isXHR = $app->request()->isAjax();
+$isXHR = $app->request()->isXhr();
+```
+
+
+
+## Helpers
+
+The Leaf application’s request object provides several helper methods to fetch common HTTP request information:
+
+## Content Type
+
+Fetch the request’s content type (e.g. “application/json;charset=utf-8”):
+
+```php
+request()->getContentType();
+```
+
+## Media Type
+
+Fetch the request’s media type (e.g. “application/json”):
+
+```php
+request()->getMediaType();
+```
+
+## Media Type Params
+
+Fetch the request’s media type parameters (e.g. [charset => “utf-8”]):
+
+```php
+request()->getMediaTypeParams();
+```
+
+## Content Charset
+
+Fetch the request’s content character set (e.g. “utf-8”):
+
+```php
+request()->getContentCharset();
+```
+
+## Content Length
+
+Fetch the request’s content length:
+
+```php
+request()->getContentLength();
+```
+
+## Host
+
+Fetch the request’s host (e.g. “leafphp.netlify.com”):
+
+```php
+request()->getHost();
+```
+
+## Host with Port
+
+Fetch the request’s host with port (e.g. “leafphp.netlify.com:80”):
+
+```php
+request()->getHostWithPort();
+```
+
+## Port
+
+Fetch the request’s port (e.g. 80):
+
+```php
+request()->getPort();
+```
+
+## Scheme
+
+Fetch the request’s scheme (e.g. “http” or “https”):
+
+```php
+request()->getScheme();
+```
+
+## Path
+
+Fetch the request’s path (root URI + resource URI):
+
+```php
+request()->getPath();
+```
+
+## URL
+
+Fetch the request’s URL (scheme + host [ + port if non-standard ]):
+
+```php
+request()->getUrl();
+```
+
+## IP Address
+
+Fetch the request’s IP address:
+
+```php
+request()->getIp();
+```
+
+## Referer
+
+Fetch the request’s referrer:
+
+```php
+request()->getReferrer();
+```
+
+## User Agent
+
+Fetch the request’s user agent string:
+
+```php
+request()->getUserAgent();
+```
+
+
+
+## Paths
+
+Every HTTP request received by a Leaf application will have a root URI and a resource URI.
+
+## Root URI
+
+The root URI is the physical URL path of the directory in which the Leaf application is instantiated and run. If a Leaf application is instantiated in **index.php** within the top-most directory of the virtual host’s document root, the root URI will be an empty string. If a Leaf application is instantiated and run in **index.php** within a physical subdirectory of the virtual host’s document root, the root URI will be the path to that subdirectory with a leading slash and without a trailing slash.
+
+## Resource URI
+
+The resource URI is the virtual URI path of an application resource. The resource URI will be matched to the Leaf application’s routes.
+
+Assume the Leaf application is installed in a physical subdirectory **/foo** beneath your virtual host’s document root. Also assume the full HTTP request URL (what you’d see in the browser location bar) is **/foo/books/1**. The root URI is /foo (the path to the physical directory in which the Leaf application is instantiated) and the resource URI is **/books/1** (the path to the application resource).
+
+You can get the HTTP request’s root URI and resource URI with the request object’s `getRootUri()` and `getResourceUri()` methods:
+
+```php
+$app = new \Leaf\App;
+
+//Get root URI
+$rootUri = $app->request()->getScriptName();
+
+//Get resource URI
+$resourceUri = $app->request()->getPathInfo();
+```
diff --git a/apps/docs/src/modules/http/v/1/response.md b/apps/docs/src/modules/http/v/1/response.md
new file mode 100755
index 0000000..5d31ccf
--- /dev/null
+++ b/apps/docs/src/modules/http/v/1/response.md
@@ -0,0 +1,314 @@
+# Leaf Response
+
+
+::: warning Watch out
+Leaf response is a class available on the leaf http module. Check out the [http module docs](/modules/http/) for installation instructions.
+:::
+
+The response object is an abstraction of your Leaf application’s HTTP response that is returned to the HTTP client. In v2, the response object has been directly bound to the main Leaf object.
+
+## Using Response
+
+### Static Response
+
+Response allows you to call methods statically, which means you don't need to initialize the whole package. If you are using the http module out of leaf, this is also the way to go.
+
+```php
+Leaf\Http\Response::json(["name" => "mychi"]);
+
+// or
+
+use Leaf\Http\Response;
+
+Response::json(["name" => "mychi"]);
+```
+
+### 🎄 Response on the Leaf Instance
+
+Since Response is already bound to the Leaf instance, you can do this:
+
+```php{4}
+$app = new Leaf\App;
+
+$app->get("/text", function () use($app) {
+ $app->response()->markup("This is text");
+});
+```
+
+Although we've added this, we don't want to force you to do stuff in only one way, so you can still use the `v1.x` method.
+
+### 🎎 Initialising the Response object
+
+With this method, you manually initialise the Response object, and then pass it into your route.
+
+```php{2,6}
+$app = new Leaf\App;
+$response = new Leaf\Http\Response;
+
+$app->post("/login", function () use($response) {
+ // ...
+ $response->json(["username" => $user]);
+});
+```
+
+An HTTP response has three primary properties:
+
+- Status
+- Header
+- Body
+
+The response object provides helper methods, described next, that help you interact with these HTTP response properties.
+
+
+
+## throwErr
+
+"What of error handling?". `throwErr` is just the right method for that. It returns JSON encoded data alongside a code just like `respondWithCode`, however unlike `respondWithCode`, `throwErr` ends the application just like an exception.
+
+You can get more info on http status codes [here](https://www.restapitutorial.com/httpstatuscodes.html).
+
+```php
+$app->post('/name', function () use($app) {
+ $name = $app->request->get("name");
+ if (!$name) $app->response()->throwErr('Name is required', 500);
+
+ // code below won't run since the app breaks on throwErr
+});
+```
+
+If no code is passed in, throwErr will send a default `500` status code.
+
+**Use message:**
+
+Just like with `respondWithCode`, `throwErr` also allows you to use messages instead of codes which most users don't understand.
+
+```php
+$response->throwErr("error", 500, true);
+```
+
+
+
+## plain
+
+This method allows you to output plain text as your response. It takes in 2 parameters:
+
+- the data to output
+- http status code with 200 default (optional)
+
+```json
+response()->plain("hello");
+```
+
+## json
+
+Json, a new method in v2.4, just as the name suggests allows you output json as a response.
+
+It takes in 4 parameters:
+
+- The data to output
+- The https status code of the data, default 200 (optional)
+- Option to show/hide the status code in response body, default `false` (optional)
+- Option to use message instead of code, default `false` (optional)
+
+```php
+$response->json("Output", 200);
+```
+
+**Output**:
+
+```json
+"Output"
+```
+
+Showing the code in body:
+
+```php
+$response->json("Output", 200, true);
+```
+
+**Output**:
+
+```json
+{
+ "data": "Output",
+ "code": 200
+}
+```
+
+Note that you can't use message and code at the same time
+
+```php
+$response->json("Output", 200, true, true);
+```
+
+**Output**:
+
+```json
+{
+ "data": "Output",
+ "message": "200 OK"
+}
+```
+
+## page
+
+This is a simple method that outputs a webpage. This method can also be used to achieve server side routing, for example:
+
+```php
+$app->get('/homepage', function () use($response) {
+ $response->page('link/to/home.html');
+});
+```
+
+With this, whenever the route `/homepage` is invoked, Leaf loads up `home.html` and outputs it to the user
+renderMarkup()
+
+**Note** `page` has **NOTHING** to do with templating, it simply outputs an already defined web page.
+
+For templating with Leaf, [look here](/modules/views/)
+
+**Status Code:**
+
+In v2.4, you can add a status code to the page response as the second parameter.
+
+```php
+$response->page("404.html", 404);
+```
+
+## markup
+
+This method outputs a string entered into it as markup with a content type of `text/html`:
+
+For instance, with this code,
+
+```php
+$code = "
Hello
";
+```
+
+We simply pass it into the response...like this
+
+```php
+$app->get('/homepage', function () use($app) {
+ $app->response()->markup($code);
+});
+```
+
+You might be wondering why we don't just use
+
+```php
+echo "
hello
";
+```
+
+The reason is, Leaf has default headers which set the content type to JSON, in order to correctly output HTML, you need to change this....Leaf has taken care of this with a bunch of other things, all within `markup` and `page`
+
+## download
+
+In v3, you can send a response which will be downloaded on the client. Note that in this case, the response should be a valid file.
+
+```php
+response()->download('file.zip', 'File name on client', 200);
+```
+
+As shown above, it takes in 3 parameters:
+
+- the file to send as response
+- The name of the file to show to client (optional, defaults to original filename)
+- Http status code (optional, defaults to 200)
+
+```php
+response()->download('item.jpg', 'Profile Pic', 200);
+```
+
+## Redirect
+
+This feature just simply allows you to send a redirect response which redirects to a different route.
+
+```php
+// $userHasAuth: true
+
+if ($userHasAuth) return $response->redirect("/home");
+```
+
+## Headers
+
+An instance of `Leaf\Http\Headers` has been included in the response object. This allows you to quickly set response headers without including the Headers package.
+
+```php
+$app = new \Leaf\App;
+$app->response()->headers->set('Content-Type', 'application/json');
+```
+
+You may also fetch `headers` from the response object’s headers property, too:
+
+```php
+$contentType = $response->headers->get('Content-Type');
+```
+
+If a header with the given name does not exist, `null` is returned. You may specify header names with upper, lower, or mixed case with dashes or underscores. Use the naming convention with which you are most comfortable.
+
+## Status
+
+::: tip Info
+You can directly set status codes on responses, there's no need to use this method unless you want to use PHP's output methods like echo
+:::
+
+The HTTP response returned to the client will have a status code indicating the response’s type (e.g. 200 OK, 400 Bad Request, or 500 Server Error). You can use the Leaf application’s response object to set the HTTP response’s status like this:
+
+```php
+$app->response()->status(400);
+```
+
+You only need to set the response object’s status if you intend to return an HTTP response that does not have a 200 OK status. You can just as easily fetch the response object’s current HTTP status by invoking the same method without an argument, like this:
+
+```php
+$status = $response->status();
+```
+
+## 🍪 Cookies
+
+You can also add a cookie using the response object. This uses Leaf Cookies.
+
+### setCookie
+
+This method uses [Leaf Cookie's set](/modules/cookies/#set)
+
+```php
+$app->response()->setCookie("name", "Michael");
+```
+
+### simpleCookie
+
+This method uses [Leaf Cookie's simpleCookie](/modules/cookies/#simplecookie)
+
+```php
+$app->response()->simpleCookie("name", "Michael", "1 day");
+```
+
+### deleteCookie
+
+This method uses [Leaf Cookie's unset](/modules/cookies/#unset)
+
+```php
+$app->response()->deleteCookie("name");
+```
+
+## Functional Mode
+
+
+Response also adds a new `response` global which allows you quickly use the response object from wherever you are.
+
+```php
+response()->json([
+ "status" => "success",
+ "data" => "Hello",
+]);
+```
+
+You can also pass data directly to the response global. This will immediately call the `json` method above.
+
+```php
+response([
+ "status" => "success",
+ "data" => "Hello",
+]);
+```
diff --git a/apps/docs/src/modules/http/v/2/cache.md b/apps/docs/src/modules/http/v/2/cache.md
new file mode 100755
index 0000000..02fda25
--- /dev/null
+++ b/apps/docs/src/modules/http/v/2/cache.md
@@ -0,0 +1,136 @@
+# Http Cache
+
+
+
+
+
+Http Cache is a new class added on the http module that allows you to perform some basic caching on leaf response handlers. After installing the http module, you can use all caching methods.
+
+
+ New to http caching?
+
+
+
+## Usage
+
+You can use caching via the `etag`, `lastModified`, and `expires` methods. It is best to use one of `etag` or `lastModified` - in conjunction with `expires` - per route; never use both `etag` and `lastModified` together in the same route callback.
+
+The `etag` and `lastModified` methods should be invoked in a route callback before other code; this allows Leaf to check conditional GET requests before processing the route callback’s remaining code.
+
+Both `etag` and `lastModified` instruct the HTTP client to store the resource response in a client-side cache. The `expires` method indicates to the HTTP client when the client-side cache should be considered stale.
+
+## `etag`
+
+An ETag is a unique identifier for a resource URI. After setting the Etag headers, the HTTP client will send an `If-None-Match` header with each subsequent HTTP request of the same resource URI. If the ETag value for the resource URI matches the `If-None-Match` HTTP request header, GET and HEAD requests will return a `304 Not Modified` HTTP response while all others return a `421 Precondition Failed` that will prompt the HTTP client to continue using its cache; this also prevents Leaf from serving the entire markup for the resource URI, saving bandwidth and response time.
+
+Setting an ETag with Leaf is very simple. Invoke Leaf’s etag method in your route callback, passing it a unique ID as the first and only argument.
+
+
+
+```php
+use \Leaf\Http\Headers;
+
+app()->get("/", function () {
+ Headers::etag("unique-tag");
+
+ echo "This will be cached after the initial request!";
+});
+```
+
+
+
+
+```php
+use \Leaf\Http\Headers;
+
+$app = new Leaf\App;
+
+$app->get("/", function () {
+ Headers::etag("unique-tag");
+
+ echo "This will be cached after the initial request!";
+});
+```
+
+
+
+That’s it. Make sure the ETag ID is unique for the given resource. Also make sure the ETag ID changes as your resource changes; otherwise, the HTTP client will continue serving its outdated cache.
+
+## `expires`
+
+Used in conjunction with the Leaf application’s etag or lastModified methods, the expires method sets an Expires header on the HTTP response informing the HTTP client when its client-side cache for the current resource should be considered stale. The HTTP client will continue serving from its client-side cache until the expiration date is reached, at which time the HTTP client will send a conditional GET request to the Leaf application.
+
+The expires method accepts one argument: an integer UNIX timestamp, or a string to be parsed with strtotime.
+
+
+
+```php
+use \Leaf\Http\Headers;
+
+app()->get("/", function () {
+ Headers::etag("unique-tag");
+ Headers::expires("+1 week");
+
+ echo "This will be cached client-side for one week";
+});
+```
+
+
+
+
+```php
+use \Leaf\Http\Headers;
+
+$app = new Leaf\App;
+
+$app->get("/", function () {
+ Headers::etag("unique-tag");
+ Headers::expires("+1 week");
+
+ echo "This will be cached client-side for one week";
+});
+```
+
+
+
+## `lastModified`
+
+A Leaf provides built-in support for HTTP caching using the resource’s last modified date. When you specify a last modified date, Leaf tells the HTTP client the date and time the current resource was last modified. The HTTP client will then send a If-Modified-Since header with each subsequent HTTP request for the given resource URI. If the last modification date you specify matches the If-Modified-Since HTTP request header, the Leaf will return a 304 Not Modified HTTP response that will prompt the HTTP client to use its cache; this also prevents the Leaf from serving the entire markup for the resource URI saving bandwidth and response time.
+
+Setting a last modified date with Leaf is very simple. You only need to invoke the Leaf’s lastModified() method in your route callback passing in a UNIX timestamp of the last modification date for the given resource. Be sure the lastModified() method’s timestamp updates along with the resource’s last modification date; otherwise, the browser client will continue serving its outdated cache.
+
+
+
+```php
+use \Leaf\Http\Headers;
+
+app()->get("/", function () {
+ Headers::lastModified(1617383991);
+
+ echo "This will be cached after the initial request!";
+});
+```
+
+
+
+
+```php
+use \Leaf\Http\Headers;
+
+$app = new Leaf\App;
+
+$app->get("/", function () {
+ Headers::lastModified(1617383991);
+
+ echo "This will be cached after the initial request!";
+});
+```
+
+
diff --git a/apps/docs/src/modules/http/v/2/headers.md b/apps/docs/src/modules/http/v/2/headers.md
new file mode 100755
index 0000000..fb8c71a
--- /dev/null
+++ b/apps/docs/src/modules/http/v/2/headers.md
@@ -0,0 +1,180 @@
+# Headers
+
+
+This is a simple object which allows you to manage the way headers are used in your application. It contains methods to set and get headers in your app.
+
+::: tip
+You can still use most header methods from within the response and request objects, you can refer to those if you want to, however, this package comes with ore features and better useability.
+:::
+
+To get started with the Headers object, you simply need to call whatever method you need on the `Leaf\Http\Headers` object. Since it's static, there's no need to initialize it.
+
+## status
+
+This method sets or returns the base HTTP status of a response. Response methods allow you to directly set http status codes, however, if you want to use PHP's native output methods, you can set the status code here.
+
+```php
+// ...
+Leaf\Http\Headers::status(404);
+echo 'Page not found';
+```
+
+You can also return the currently set status code.
+
+```php
+$code = Leaf\Http\Headers::status();
+```
+
+## `resetStatus`
+
+If for some reason, you're not able to set the status using `status`, you can always fallback to `resetStatus`. This method uses PHP's inbuilt `http_response_code`.
+
+```php
+// ...
+Leaf\Http\Headers::resetStatus(200);
+echo 'Something here';
+```
+
+## `all`
+
+This method returns all headers passed into your Leaf app. It takes in a single optional parameter, whether to sanitize header data or not, it is set to false by default.
+
+```php
+// will not sanitize headers
+Leaf\Http\Headers::all();
+
+// will not sanitize headers
+Leaf\Http\Headers::all(false);
+
+// will sanitize headers
+Leaf\Http\Headers::all(true);
+```
+
+## `get`
+
+This method as the name implies returns a particular header.
+
+```php
+$content = Leaf\Http\Headers::get('Content-Type');
+```
+
+You can also get multiple headers at the same time.
+
+```php
+$headerGroup = Leaf\Http\Headers::get(['Content-Type', 'Authorization']);
+```
+
+Just like `all`, you can also sanitize the information from `get`.
+
+```php
+$data = Leaf\Http\Headers::get('header', true);
+```
+
+## `set`
+
+`set` allows you to add a new response header. It takes in 4 parameters:
+
+- The header to to set
+- Value for header
+- Replace similar header?
+- An http status code
+
+```php
+Leaf\Http\Headers::set('location', '/home', true, 302);
+```
+
+You can also set multiple values at once.
+
+```php
+Leaf\Http\Headers::set(['location' => '/home', 'something' => 'here']);
+```
+
+If you want multiple headers with the same name, you can set replace to `false`. This will force multiple headers of the same type.
+
+```php
+Leaf\Http\Headers::set([
+ 'WWW-Authenticate' => 'Negotiate',
+ 'WWW-Authenticate' => 'NTLM'
+], null, false);
+```
+
+## `remove`
+
+This method removes previously set headers.
+
+```php
+// single value
+Leaf\Http\Headers::remove('WWW-Authenticate');
+
+// multiple value
+Leaf\Http\Headers::remove(['Content-Type', 'WWW-Authenticate']);
+```
+
+## `has`
+
+
+This method allows you to check if a header has been set in the current request. It returns `true` if the header has been set and `false` otherwise.
+
+```php
+if (Leaf\Http\Headers::has('X-SOME-HEADER')) {
+ // do something nice
+}
+```
+
+## Utility Header methods
+
+Some shortcut methods have been prepared for the most used headers, so you won't need to stress yourself writing a bunch of stuff for simple tasks.
+
+### contentPlain
+
+This method set's the content type of the response to `text/plain`, it also takes in an HTTP status code.
+
+```php
+Leaf\Http\Headers::contentPlain(200);
+echo 'plain text here';
+```
+
+### contentHtml
+
+This method set's the content type of the response to `text/html`, it also takes in an HTTP status code.
+
+```php
+Leaf\Http\Headers::contentHtml(200);
+echo 'html here';
+```
+
+### contentXml
+
+This method set's the content type of the response to `application/xml`, it also takes in an HTTP status code.
+
+```php
+Leaf\Http\Headers::contentXml(200);
+echo 'Xml here';
+```
+
+### contentJSON
+
+This method set's the content type of the response to `application/json`, it also takes in an HTTP status code.
+
+```php
+Leaf\Http\Headers::contentJSON(200);
+echo 'json here';
+```
+
+### accessControl
+
+This method allows you to quickly set `Access-Control` headers in your app. It takes in 3 parameters:
+
+- The header to set
+- The value to set
+- A status code (optional)
+
+```php
+Leaf\Http\Headers::accessControl('Allow-Origin', 'https://example.com', 200);
+```
+
+You can set mutiple access control headers at once:
+
+```php
+Leaf\Http\Headers::accessControl(['Allow-Origin' => '*', 'Allow-Headers' => '*']);
+```
diff --git a/apps/docs/src/modules/http/v/2/index.md b/apps/docs/src/modules/http/v/2/index.md
new file mode 100644
index 0000000..86ab32c
--- /dev/null
+++ b/apps/docs/src/modules/http/v/2/index.md
@@ -0,0 +1,61 @@
+# Leaf Http v2
+
+::: warning
+There is no need to manually add the Http module if you're using Leaf 3 since this is done for you automatically.
+:::
+
+The Leaf Http module contains a bunch of handlers for managing the kinds and methods through which data flows in and out of your application.
+
+The available classes in the Http module are:
+
+
+
+- [`Leaf\Http\Request`](/modules/http/v/2/request)
+- [`Leaf\Http\Response`](/modules/http/v/2/response)
+- [`Leaf\Http\Headers`](/modules/http/v/2/headers)
+- [`Leaf\Http\Cache`](/modules/http/v/2/cache)
+- [`Leaf\Http\Status`](/modules/http/v/2/status)
+
+## Installation
+
+You can install the http module with the [leaf cli](/docs/cli/):
+
+```bash
+leaf install http
+```
+
+or with composer:
+
+```bash
+composer require leafs/http
+```
+
+From there you can use any of the classes above in your project.
+
+::: tip
+Cookies and session are independent modules which are not added to the Http module. This is because, the use of session and cookies is relatively low in APIs. If you however want to use sessions and cookies, you can read their guides for information on them.
+:::
+
+## Request
+
+This is a developer friendly interface which allows you to interact with data coming into your application. [Read the docs](/modules/http/v/2/request)
+
+## Response
+
+This interface allows you to output data from your application in different forms. [Read the docs](/modules/http/v/2/response)
+
+## Headers
+
+This interface allows you to manage headers in your application. [Read the docs](/modules/http/v/2/headers)
+
+## Cache
+
+This interface allows you to manage http cache in your app. [Read the docs](/modules/http/v/2/cache)
+
+## Session (module)
+
+This module allows you to manage session in your application. [Read the docs](/modules/session/)
+
+## Cookies (module)
+
+This module allows you to manage cookies in your application. [Read the docs](/modules/cookies/)
diff --git a/apps/docs/src/modules/http/v/2/request.md b/apps/docs/src/modules/http/v/2/request.md
new file mode 100755
index 0000000..c19e79b
--- /dev/null
+++ b/apps/docs/src/modules/http/v/2/request.md
@@ -0,0 +1,962 @@
+# Request
+
+
+
+
+The request object provides an interface for accessing and manipulating the current HTTP request being handled by your application, as well as retrieving input, cookies, and files that were submitted with the request.
+
+## Using the request object
+
+There are different ways you can access an instance of the Leaf request object. We've listed a couple of them below, every method below will return the active instance of Leaf request.
+
+
+
+### Functional Mode
+
+
+
+Request now hooks into leaf 3's functional mode and comes with global functions you can use anywhere in your app. Read the [functional mode docs](/docs/tooling/functions) for all the information on functional mode.
+
+```php{2}
+app()->post('/items/add', function () {
+ echo request()->get('username');
+});
+```
+
+As you noticed above, we simply call the `request` method without doing anything. Everything is taken care of for us. Also, now, you can use this feature even when you are not using Leaf in your app.
+
+
+
+
+### Request class
+
+The request class allows you to quickly access all the features of leaf request.
+
+```php
+Leaf\Http\Request::get('name');
+
+// or
+
+use Leaf\Http\Request;
+
+Request::get('name');
+```
+
+
+
+### Request on the Leaf Instance
+
+If you are using request in a leaf app, leaf automatically binds the request class to the leaf instance, so you can always access the leaf request object without having to include any classes or namespaces.
+
+
+
+```php{2}
+app()->post('/user/change-username', function () {
+ echo app()->request()->get('username');
+});
+```
+
+Although you can do this, there's no need to go with this method since you have access to the `request` global.
+
+
+
+
+```php{4}
+$app = new Leaf\App;
+
+$app->post('/user/change-username', function () use($app) {
+ echo $app->request()->get('username');
+});
+```
+
+
+
+## Basic Request Information
+
+The request instance has several methods that allow you to inspect the HTTP request made to your application. Some useful methods include:
+
+### `get`
+
+`get()` is a general purpose method which retrieves a particular item from the request body. In simpler terms, it works like `$_POST['key']` but works for all request types. It takes in one parameter: the key of the parameter you wish to get.
+
+
+
+#### Multiple select
+
+In v2.4, you can retrieve a couple of fields you want, and not just one. You can also use this as a filter to return only the data you want in your app instead of using `body` which dumps all request data.
+
+
+
+```php
+$loginData = $app->request()->get(['username', 'password']);
+// ... do something with username
+echo $loginData['username'];
+```
+
+
+
+
+```php
+$loginData = request()->get(['username', 'password']);
+// ... do something with username
+echo $loginData['username'];
+```
+
+
+
+This allows you to set data you need dynamically.
+
+
+
+```php
+list($username, $password) = array_values($app->request()->get(['username', 'password']));
+// ... do something with username
+echo $username;
+```
+
+
+
+
+```php
+list($username, $password) = array_values(request()->get(['username', 'password']));
+// ... do something with username
+echo $username;
+```
+
+
+
+#### Security Fixes
+
+`get()` has also received a bunch of security fixes which prevent malicious scripts from being passed into your application. In v2.4, you can choose not to sanitize data coming into your application by passing in `false` as the second parameter.
+
+
+
+```php
+// data is sanitized
+$username = $app->request()->get('username');
+// data is sanitized
+$title = $app->request()->get('title', true);
+// data is not sanitized
+$blog = $app->request()->get('blogBody', false);
+```
+
+
+
+
+```php
+// data is sanitized
+$username = request()->get('username');
+// data is sanitized
+$title = request()->get('title', true);
+// data is not sanitized
+$blog = request()->get('blogBody', false);
+```
+
+
+
+### `try`
+
+`try()` works just like `get` above, except that it conditionally returns items in the request. Let's look at an example:
+
+
+
+Unlike `get` and `body`, if the parameter to find in the request is not found, it will automatically be removed from the data returned. You can also remove empty strings from the request by passing `true` as a third parameter.
+
+The available parameters are:
+
+- array - The parameters to return
+- bool - Sanitize output? Default `true`
+- bool - Remove empty strings from return data? Default `false`
+
+### `params`
+
+Params is another method which works just like the `get` method above, however, unlike `get` and `try` above, it allows you to specify defaults for items in case they are not found. It also does NOT support multiple select.
+
+
+
+In case `description` was not passed into the request above, Leaf will return `No Description` instead of an null field.
+
+### `body`
+
+`body()` is another general purpose method which retrieves the key => value pairs of the entire request body. In simpler terms, it works like `$_POST` but works for all request types. In v2.4, `body` can also retrieve files passed into the request.
+
+
+
+#### Security Fixes
+
+`body` has also received a bunch of security fixes which prevent maliscious scripts from being passed into your application. It accepts a boolean option which determines if the data coming into your application is sanitized or not. This means that you can turn off the sanitization in case you trust the source of data. By default, this option is enabled.
+
+
+
+```php
+// data is sanitized
+$body = $app->request()->body();
+
+// data is sanitized
+$body = $app->request()->body(true);
+
+// data is not sanitized
+$body = $app->request()->body(false);
+```
+
+
+
+
+```php
+// data is sanitized
+$body = request()->body();
+
+// data is sanitized
+$body = request()->body(true);
+
+// data is not sanitized
+$body = request()->body(false);
+```
+
+
+
+### `files`
+
+You may access uploaded files that are included with the request using the `files` method. This returns the raw file to you:
+
+
+
+### `rawData`
+
+This method allows you to access the raw PHP input stream only. This works with requests like JSON and xml-http requests. It takes in a string or array of the data you want to retrieve and the default if that data isn't found.
+
+
+
+### `urlData`
+
+This method allows you to access GET request data only. It takes in a string or array of the data you want to retrieve and the default if that data isn't found.
+
+
+
+### `postData`
+
+This method allows you to access the post request data only. It takes in a string or array of the data you want to retrieve and the default if that data isn't found.
+
+
+
+## Validating Request Data new
+
+Leaf Request now comes with a built-in validator which allows you to validate request data directly on the request object. You don't need to install or configure anything. To get started, all you need to do is call `validate()` on the request object.
+
+
+
+## Request Headers and Cookies
+
+The request instance also contains methods which allow you retrieve headers and cookies from the incoming request.
+
+### Headers
+
+A Leaf application will automatically parse all HTTP request headers. You can access the request headers using the request object's `headers` method.
+
+
+
+```php
+// Get request headers as associative array
+$headers = $app->request()->headers();
+
+// Get the ACCEPT_CHARSET header
+$charset = $app->request()->headers('ACCEPT_CHARSET');
+
+// Get some specific headers as an array
+$headers = $app->request()->headers(['ACCEPT_CHARSET', 'X-Header-Name']);
+```
+
+
+
+
+```php
+// Get request headers as associative array
+$headers = request()->headers();
+
+// Get the ACCEPT_CHARSET header
+$charset = request()->headers('ACCEPT_CHARSET');
+
+// Get some specific headers as an array
+$headers = request()->headers(['ACCEPT_CHARSET', 'X-Header-Name']);
+```
+
+
+
+Note that Leaf will automatically sanitize the headers that come into your application. This means that you don't have to worry about malicious scripts being passed into your application. If you however want to disable this feature, you can pass in a boolean option to the second field of the `headers` method. By default, this option is enabled.
+
+
+
+The HTTP specification states that HTTP header names may be uppercase, lowercase, or mixed-case. Leaf is smart enough to parse and return header values whether you request a header value using upper, lower, or mixed case header name, with either underscores or dashes. So use the naming convention with which you are most comfortable.
+
+### Cookies
+
+Leaf also provides a `cookies` method on the request object which allows you to get cookie data.
+
+
+
+```php
+// get specific cookie
+$app->request()->cookies('name');
+
+// get all cookies
+$app->request()->cookies();
+```
+
+
+
+
+```php
+// get specific cookie
+request()->cookies('name');
+
+// get all cookies
+request()->cookies();
+```
+
+
+
+## Request Method functions
+
+Every HTTP request has a method (e.g. GET or POST). You can obtain the current HTTP request method via the Leaf application's request object:
+
+### `typeIs`
+
+This method allows you to check what method type a request uses.
+
+
+
+Here are some other functions you can use relating to the request method.
+
+
+
+```php
+/**
+ * What is the request method?
+ * @return string (e.g. GET, POST, PUT, DELETE)
+ */
+$app->request()->getMethod();
+```
+
+
+
+
+```php
+/**
+ * What is the request method?
+ * @return string (e.g. GET, POST, PUT, DELETE)
+ */
+request()->getMethod();
+```
+
+
+
+### XHR
+
+When using a Javascript framework like MooTools or jQuery to execute an XMLHttpRequest, the XMLHttpRequest will usually be sent with a **`X-Requested-With`** HTTP header. The Leaf application will detect the HTTP request’s **`X-Requested-With`** header and flag the request as such. If for some reason an XMLHttpRequest cannot be sent with the **`X-Requested-With`** HTTP header, you can force the Leaf application to assume an HTTP request is an XMLHttpRequest by setting a GET, POST, or PUT parameter in the HTTP request named “isajax” with a truthy value.
+
+Use the request object’s `isAjax()` or `isXhr()` method to tell if the current request is an XHR/Ajax request:
+
+
+
+## Request Path, Host & Client
+
+This section contains methods which allow you to retrieve information about the request path, host and client.
+
+### Host
+
+Fetch the request’s host (e.g. “leafphp.dev”):
+
+
+
+```php
+request()->getHost();
+```
+
+
+
+
+```php
+$app->request()->getHost();
+```
+
+
+
+### Host with Port
+
+Fetch the request’s host with port (e.g. “leafphp.dev:80”):
+
+
+
+### Paths
+
+Every HTTP request received by a Leaf application will have a root URI and a resource URI.
+
+#### Root URI
+
+The root URI is the physical URL path of the directory in which the Leaf application is instantiated and run. If a Leaf application is instantiated in **index.php** within the top-most directory of the virtual host’s document root, the root URI will be an empty string. If a Leaf application is instantiated and run in **index.php** within a physical subdirectory of the virtual host’s document root, the root URI will be the path to that subdirectory with a leading slash and without a trailing slash.
+
+#### Resource URI
+
+The resource URI is the virtual URI path of an application resource. The resource URI will be matched to the Leaf application’s routes.
+
+Assume the Leaf application is installed in a physical subdirectory **/foo** beneath your virtual host’s document root. Also assume the full HTTP request URL (what you’d see in the browser location bar) is **/foo/books/1**. The root URI is /foo (the path to the physical directory in which the Leaf application is instantiated) and the resource URI is **/books/1** (the path to the application resource).
+
+You can get the HTTP request’s root URI and resource URI with the request object’s `getScriptName()` and `getPathInfo()` methods:
+
+
+
+```php
+$app = new \Leaf\App;
+
+//Get root URI
+$rootUri = $app->request()->getScriptName();
+
+//Get resource URI
+$resourceUri = $app->request()->getPathInfo();
+```
+
+
+
+
+```php
+//Get root URI
+$rootUri = request()->getScriptName();
+
+//Get resource URI
+$resourceUri = request()->getPathInfo();
+```
+
+
+
+## Content Type Methods
+
+The Leaf application’s request object provides several helper methods for inspecting the content type of the current HTTP request.
+
+### Content Type
+
+Fetch the request’s content type (e.g. “application/json;charset=utf-8”):
+
+
diff --git a/apps/docs/src/modules/http/v/2/response.md b/apps/docs/src/modules/http/v/2/response.md
new file mode 100755
index 0000000..a35999e
--- /dev/null
+++ b/apps/docs/src/modules/http/v/2/response.md
@@ -0,0 +1,638 @@
+# Response
+
+
+The response object is an abstraction of your Leaf application’s HTTP response that is returned to the HTTP client.
+
+## Using the Response object
+
+Leaf offers a couple of ways to use the response object in your application.
+
+### Response on the Leaf Instance
+
+Since Response is already bound to the Leaf instance, you can do this:
+
+
+
+```php{4}
+$app = new Leaf\App;
+
+$app->get("/text", function () use($app) {
+ $app->response()->markup("This is text");
+});
+```
+
+
+
+
+```php{2}
+app()->get('/text', function () {
+ app()->response()->markup('This is text');
+});
+```
+
+
+
+Although we've added this, we don't want to force you to do stuff in only one way, so you can still use the `v1.x` method.
+
+
+
+### Initialising the Response object
+
+With this method, you manually initialise the Response object, and then pass it into your route.
+
+```php{2,6}
+$app = new Leaf\App;
+$response = new Leaf\Http\Response;
+
+$app->post('/login', function () use($response) {
+ // ...
+ $response->json(['username' => $user]);
+});
+```
+
+
+
+
+### Functional Mode
+
+Response also takes advantage of Leaf 3's functional mode with the `response` global which allows you quickly use the response object from wherever you are.
+
+```php
+response()->json([
+ 'status' => 'success',
+ 'data' => 'Hello',
+]);
+```
+
+
+
+An HTTP response has three primary properties:
+
+- Status
+- Header
+- Body
+
+The response object provides helper methods, described next, that help you interact with these HTTP response properties.
+
+## Method Chaining
+
+Method chaining allows you to be more expressive with your code and basically fit everything better. There's just a single rule you need to follow here: ***the method you want to output should be the last thing you call.***
+
+If you want to output some JSON with a header `something`, you should always set the header before calling the JSON method.
+
+
+
+## Response Methods
+
+This section covers all methods provided in the response object which allow you to output some kind of data.
+
+### `plain`
+
+This method allows you to output plain text as your response. It takes in 2 parameters:
+
+- the data to output
+- http status code with 200 default (optional)
+
+
+
+### `xml`
+
+This method allows you to output xml as your response. It takes in 2 parameters:
+
+- the data to output
+- http status code with 200 default (optional)
+
+
+
+### `json`
+
+This method allows you output json as a response.
+
+It takes in 3 parameters:
+
+- The data to output
+- The https status code of the data, default 200 (optional)
+- Option to show/hide the status code in response body, default `false` (optional)
+
+
+
+**Output**:
+
+```json
+{
+ "data": "Output",
+ "status": {
+ "code": 200,
+ "message": "OK"
+ }
+}
+```
+
+### `page`
+
+This is a method that outputs an HTML/PHP file. This method can also be used to achieve server side routing, for example:
+
+
+
+With this, whenever the route `/homepage` is invoked, Leaf loads up `home.html` and outputs it to the user.
+
+**Note** The `page` method has **NOTHING** to do with templating, it simply outputs an already defined web page.
+
+For templating with Leaf, [look here](/modules/views/)
+
+**Status Code:**
+
+It takes in a status code as the second parameter.
+
+
+
+```php
+$app->get('/homepage', function () use($app) {
+ $app->response()->markup('
Hello
');
+});
+```
+
+
+
+
+```php
+app()->get('/homepage', function () {
+ response()->markup('
Hello
');
+});
+```
+
+
+
+You might be wondering why we don't just use
+
+```php
+echo '
hello
';
+```
+
+The reason is, Leaf has default headers which set the content type to JSON, in order to correctly output HTML, you need to change this.... Leaf has taken care of this with a bunch of other things, all within `markup` and `page`.
+
+You can also specify a status code:
+
+
+
+```php
+$app->response()->markup('
Hello
', 201);
+```
+
+
+
+
+```php
+response()->markup('
Hello
', 201);
+```
+
+
+
+### `download`
+
+In v3, you can send a response which will be downloaded on the client. Note that in this case, the response should be a valid file.
+
+
+
+```php
+// using defaults
+$app->response()->download('path/to/file.zip');
+
+// syntax
+$app->response()->download('path/to/file.zip', 'File name on client', 200);
+```
+
+
+
+
+```php
+// using defaults
+response()->download('path/to/file.zip');
+
+// syntax
+response()->download('path/to/file.zip', 'File name on client', 200);
+```
+
+
+
+As shown above, it takes in 3 parameters:
+
+- the file to send as response
+- The name of the file to show to client (optional, defaults to original filename)
+- Http status code (optional, defaults to 200)
+
+
+
+### `noContent`
+
+The HTTP 204 No Content success status response code indicates that a request has succeeded, but that the client doesn't need to navigate away from its current page. This method allows you to quickly create a 204 response.
+
+
+
+### `exit`
+
+This is a new method which allows you to output some data and close your app right after. This means that it acts as a sort of early-return for your app, so right after outputting some data, it quits and makes sure that no other code is executed from your app until the next request comes through.
+
+It takes in 2 parameters: the data to output and the http status code (default: 500).
+
+
+
+```php
+$app->response()->exit('This will be output as markup');
+
+// code below won't run
+```
+
+
+
+
+```php
+response()->exit('This will be output as markup');
+
+// code below won't run
+```
+
+
+
+You can also output JSON.
+
+
+
+```php
+$app->response()->exit(['data' => 'This will be output as JSON'], 500);
+```
+
+
+
+
+```php
+response()->exit(['data' => 'This will be output as JSON'], 500);
+```
+
+
+
+## Headers
+
+Headers are a way for your server to send additional information along with your request. This information can be anything from the type of content you're sending back, to the status code of your response, to the type of server you're using.
+
+Leaf allows you to set headers for your response directly from the response object using the `withHeader()` method. It takes in 4 parameters:
+
+- The header name or an array of headers (key-value pairs)
+- The header value if header key is a string
+- A boolean on whether to replace the header if it's already set
+- An Http status code to associate to header.
+
+
+
+## Cookies
+
+Cookies are small pieces of data that are stored on the client's computer by the web browser while browsing a website. Cookies were designed to be a reliable mechanism for websites to remember stateful information or to record the user's browsing activity.
+
+Leaf allows you to set cookies for your response using the `withCookie()` method. It takes in 3 parameters:
+
+- The name of the cookie
+- The value of cookie
+- When the cookie expires. Default: 7 days
+
+
+
+### `withoutCookie()`
+
+This method allows you to remove existing cookies from your response. So you're basically returning a response without selected cookies.
+
+
+
+## Flash messaging
+
+Flash messages are a way to keep a message around for a single request. They're helpful for displaying status messages like "Item deleted successfully" or "Your changes have been saved."
+
+Leaf allows you to set flash messages for your response using the `withFlash()` method. It takes in 2 parameters:
+
+- The name of the flash message
+- The value of the flash message
+
+
+
+## Status
+
+::: tip Info
+You can directly set status codes on responses, however, you can use this method to declaratively set a status code for you Leaf responses or if you want to use PHP's output methods like echo
+:::
+
+The HTTP response returned to the client will have a status code indicating the response’s type (e.g. 200 OK, 400 Bad Request, or 500 Server Error). You can use the Leaf application’s response object to set the HTTP response’s status like this:
+
+
+
+```php
+$app->response()->status(400);
+
+echo 'This is the main output';
+```
+
+
+
+
+```php
+response()->status(400);
+
+echo 'This is the main output';
+```
+
+
+
+You only need to set the response object’s status if you intend to return an HTTP response that does not have a 200 OK status.
diff --git a/apps/docs/src/modules/http/v/2/status.md b/apps/docs/src/modules/http/v/2/status.md
new file mode 100755
index 0000000..9fdad3b
--- /dev/null
+++ b/apps/docs/src/modules/http/v/2/status.md
@@ -0,0 +1,156 @@
+# Http Status Helper
+
+
+
+This is an http status code helper created in attempt to refactor the response object a bit and speed up the whole Http module flow. It provides various constants and messages for various status codes.
+
+## List of constants
+
+**Usage:**
+
+```php
+use Leaf\Http\Status;
+
+echo Status::HTTP_CONTINUE; // 100
+```
+
+```php
+public const HTTP_CONTINUE = 100;
+public const HTTP_SWITCHING_PROTOCOLS = 101;
+public const HTTP_PROCESSING = 102; // RFC2518
+public const HTTP_EARLY_HINTS = 103; // RFC8297
+public const HTTP_OK = 200;
+public const HTTP_CREATED = 201;
+public const HTTP_ACCEPTED = 202;
+public const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
+public const HTTP_NO_CONTENT = 204;
+public const HTTP_RESET_CONTENT = 205;
+public const HTTP_PARTIAL_CONTENT = 206;
+public const HTTP_MULTI_STATUS = 207; // RFC4918
+public const HTTP_ALREADY_REPORTED = 208; // RFC5842
+public const HTTP_IM_USED = 226; // RFC3229
+public const HTTP_MULTIPLE_CHOICES = 300;
+public const HTTP_MOVED_PERMANENTLY = 301;
+public const HTTP_FOUND = 302;
+public const HTTP_SEE_OTHER = 303;
+public const HTTP_NOT_MODIFIED = 304;
+public const HTTP_USE_PROXY = 305;
+public const HTTP_RESERVED = 306;
+public const HTTP_TEMPORARY_REDIRECT = 307;
+public const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238
+public const HTTP_BAD_REQUEST = 400;
+public const HTTP_UNAUTHORIZED = 401;
+public const HTTP_PAYMENT_REQUIRED = 402;
+public const HTTP_FORBIDDEN = 403;
+public const HTTP_NOT_FOUND = 404;
+public const HTTP_METHOD_NOT_ALLOWED = 405;
+public const HTTP_NOT_ACCEPTABLE = 406;
+public const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
+public const HTTP_REQUEST_TIMEOUT = 408;
+public const HTTP_CONFLICT = 409;
+public const HTTP_GONE = 410;
+public const HTTP_LENGTH_REQUIRED = 411;
+public const HTTP_PRECONDITION_FAILED = 412;
+public const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
+public const HTTP_REQUEST_URI_TOO_LONG = 414;
+public const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
+public const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+public const HTTP_EXPECTATION_FAILED = 417;
+public const HTTP_I_AM_A_TEAPOT = 418; // RFC2324
+public const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540
+public const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
+public const HTTP_LOCKED = 423; // RFC4918
+public const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
+public const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04
+public const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
+public const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
+public const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
+public const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
+public const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
+public const HTTP_INTERNAL_SERVER_ERROR = 500;
+public const HTTP_NOT_IMPLEMENTED = 501;
+public const HTTP_BAD_GATEWAY = 502;
+public const HTTP_SERVICE_UNAVAILABLE = 503;
+public const HTTP_GATEWAY_TIMEOUT = 504;
+public const HTTP_VERSION_NOT_SUPPORTED = 505;
+public const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295
+public const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918
+public const HTTP_LOOP_DETECTED = 508; // RFC5842
+public const HTTP_NOT_EXTENDED = 510; // RFC2774
+public const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
+```
+
+## List of Status Texts
+
+**Usage:**
+
+```php
+use Leaf\Http\Status;
+
+echo Status::$statusTexts[200]; // OK
+```
+
+```php
+100 => 'Continue',
+101 => 'Switching Protocols',
+102 => 'Processing', // RFC2518
+103 => 'Early Hints',
+200 => 'OK',
+201 => 'Created',
+202 => 'Accepted',
+203 => 'Non-Authoritative Information',
+204 => 'No Content',
+205 => 'Reset Content',
+206 => 'Partial Content',
+207 => 'Multi-Status', // RFC4918
+208 => 'Already Reported', // RFC5842
+226 => 'IM Used', // RFC3229
+300 => 'Multiple Choices',
+301 => 'Moved Permanently',
+302 => 'Found',
+303 => 'See Other',
+304 => 'Not Modified',
+305 => 'Use Proxy',
+307 => 'Temporary Redirect',
+308 => 'Permanent Redirect', // RFC7238
+400 => 'Bad Request',
+401 => 'Unauthorized',
+402 => 'Payment Required',
+403 => 'Forbidden',
+404 => 'Not Found',
+405 => 'Method Not Allowed',
+406 => 'Not Acceptable',
+407 => 'Proxy Authentication Required',
+408 => 'Request Timeout',
+409 => 'Conflict',
+410 => 'Gone',
+411 => 'Length Required',
+412 => 'Precondition Failed',
+413 => 'Content Too Large', // RFC-ietf-httpbis-semantics
+414 => 'URI Too Long',
+415 => 'Unsupported Media Type',
+416 => 'Range Not Satisfiable',
+417 => 'Expectation Failed',
+418 => 'I\'m a teapot', // RFC2324
+421 => 'Misdirected Request', // RFC7540
+422 => 'Unprocessable Content', // RFC-ietf-httpbis-semantics
+423 => 'Locked', // RFC4918
+424 => 'Failed Dependency', // RFC4918
+425 => 'Too Early', // RFC-ietf-httpbis-replay-04
+426 => 'Upgrade Required', // RFC2817
+428 => 'Precondition Required', // RFC6585
+429 => 'Too Many Requests', // RFC6585
+431 => 'Request Header Fields Too Large', // RFC6585
+451 => 'Unavailable For Legal Reasons', // RFC7725
+500 => 'Internal Server Error',
+501 => 'Not Implemented',
+502 => 'Bad Gateway',
+503 => 'Service Unavailable',
+504 => 'Gateway Timeout',
+505 => 'HTTP Version Not Supported',
+506 => 'Variant Also Negotiates', // RFC2295
+507 => 'Insufficient Storage', // RFC4918
+508 => 'Loop Detected', // RFC5842
+510 => 'Not Extended', // RFC2774
+511 => 'Network Authentication Required', // RFC6585
+```
diff --git a/apps/docs/src/modules/index.md b/apps/docs/src/modules/index.md
new file mode 100644
index 0000000..167376e
--- /dev/null
+++ b/apps/docs/src/modules/index.md
@@ -0,0 +1,80 @@
+# Introduction
+
+
+
+
+
+Modules are the pieces of Leaf's functionality that are individually available as packages. They can be used in a wide variety of projects, and are one of the primary ways that Leaf is extended with additional functionality.
+
+Most modules are framework/library agnostic, which means that they'll work just about everywhere with zero config just as with Leaf itself. You can easily install them with composer or the leaf cli.
+
+## Why modules?
+
+We have a ton of reasons for switching to modules that we covered in [this blog post](https://blog.leafphp.dev/posts/leaf3.html). However, here are a few of the main reasons:
+
+- Modules are easier to update and maintain
+- Modules allow you to use only the parts of Leaf you need
+- Modules allow you to incrementally upgrade your Leaf apps
+- Modules allow you to use Leaf with other frameworks
+
+## Installing modules
+
+
+
+You can quickly install any module with the Leaf CLI:
+
+```bash
+leaf install
+```
+
+Or with composer:
+
+```bash
+composer require leafs/
+```
+
+## List of available modules
+
+*We update this list whenever we add new modules, you can keep checking for updates.*
+
+| Project | Status | Description |
+| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
+| [alchemy](/docs/tooling/testing) | [![Latest Stable Version](https://poser.pugx.org/leafs/alchemy/v/stable)](https://packagist.org/packages/leafs/alchemy) [![Total Downloads](https://poser.pugx.org/leafs/alchemy/downloads)](https://packagist.org/packages/leafs/alchemy) | Simpler tests for your PHP apps |
+| [aloe](/aloe-cli/) | [![Latest Stable Version](https://poser.pugx.org/leafs/aloe/v/stable)](https://packagist.org/packages/leafs/aloe) [![Total Downloads](https://poser.pugx.org/leafs/aloe/downloads)](https://packagist.org/packages/leafs/aloe) | Smart console helper for leaf mvc, leaf api and skeleton |
+| [anchor](/modules/anchor/) | [![Latest Stable Version](https://poser.pugx.org/leafs/anchor/v/stable)](https://packagist.org/packages/leafs/anchor) [![Total Downloads](https://poser.pugx.org/leafs/anchor/downloads)](https://packagist.org/packages/leafs/anchor) | Basic security tools |
+| [auth](/modules/auth/) | [![Latest Stable Version](https://poser.pugx.org/leafs/auth/v/stable)](https://packagist.org/packages/leafs/auth) [![Total Downloads](https://poser.pugx.org/leafs/auth/downloads)](https://packagist.org/packages/leafs/auth) | Simple but powerful authentication system for your apps |
+| [bareui](/modules/views/bareui/) | [![Latest Stable Version](https://poser.pugx.org/leafs/bareui/v/stable)](https://packagist.org/packages/leafs/bareui) [![Total Downloads](https://poser.pugx.org/leafs/bareui/downloads)](https://packagist.org/packages/leafs/bareui) | Dead simple templating engine with no compilation (blazing speed) |
+| [blade](/modules/views/blade/) | [![Latest Stable Version](https://poser.pugx.org/leafs/blade/v/stable)](https://packagist.org/packages/leafs/blade) [![Total Downloads](https://poser.pugx.org/leafs/blade/downloads)](https://packagist.org/packages/leafs/blade) | Laravel blade templating port for leaf |
+| [cookie](/modules/cookies/) | [![Latest Stable Version](https://poser.pugx.org/leafs/cookie/v/stable)](https://packagist.org/packages/leafs/cookie) [![Total Downloads](https://poser.pugx.org/leafs/cookie/downloads)](https://packagist.org/packages/leafs/cookie) | Cookie management without the tears |
+| [cors](/modules/cors/) | [![Latest Stable Version](https://poser.pugx.org/leafs/cors/v/stable)](https://packagist.org/packages/leafs/cors) [![Total Downloads](https://poser.pugx.org/leafs/cors/downloads)](https://packagist.org/packages/leafs/cors) | CORS operations made simple |
+| [csrf](/modules/anchor/csrf/) | [![Latest Stable Version](https://poser.pugx.org/leafs/csrf/v/stable)](https://packagist.org/packages/leafs/csrf) [![Total Downloads](https://poser.pugx.org/leafs/csrf/downloads)](https://packagist.org/packages/leafs/csrf) | Basic CSRF protection |
+| [date](/modules/date/) | [![Latest Stable Version](https://poser.pugx.org/leafs/date/v/stable)](https://packagist.org/packages/leafs/date) [![Total Downloads](https://poser.pugx.org/leafs/date/downloads)](https://packagist.org/packages/leafs/date) | PHP dates for humans |
+| [db](/modules/db/) | [![Latest Stable Version](https://poser.pugx.org/leafs/db/v/stable)](https://packagist.org/packages/leafs/db) [![Total Downloads](https://poser.pugx.org/leafs/db/downloads)](https://packagist.org/packages/leafs/db) | Leaf Db from v2 (actively maintained) |
+| [db-old](/modules/db-old/) | [![Latest Stable Version](https://poser.pugx.org/leafs/db-old/v/stable)](https://packagist.org/packages/leafs/db-old) [![Total Downloads](https://poser.pugx.org/leafs/db-old/downloads)](https://packagist.org/packages/leafs/db-old) | Leaf Db from v1 (still maintained) |
+| [devtools](/modules/devtools/) | [![Latest Stable Version](https://poser.pugx.org/leafs/devtools/v/stable)](https://packagist.org/packages/leafs/devtools) [![Total Downloads](https://poser.pugx.org/leafs/devtools/downloads)](https://packagist.org/packages/leafs/devtools) | Developer tools for Leaf PHP |
+| [eien](/modules/eien/) | [![Latest Stable Version](https://poser.pugx.org/leafs/eien/v/stable)](https://packagist.org/packages/leafs/eien) [![Total Downloads](https://poser.pugx.org/leafs/eien/downloads)](https://packagist.org/packages/leafs/eien) | High-speed, high-performance server for leaf |
+| [exception](https://github.com/leafsphp/exceptions) | [![Latest Stable Version](https://poser.pugx.org/leafs/exception/v/stable)](https://packagist.org/packages/leafs/exception) [![Total Downloads](https://poser.pugx.org/leafs/exception/downloads)](https://packagist.org/packages/leafs/exception) | Leaf's exception wrapper (fork of whoops) |
+| [fetch](/modules/fetch/) | [![Latest Stable Version](https://poser.pugx.org/leafs/fetch/v/stable)](https://packagist.org/packages/leafs/fetch) [![Total Downloads](https://poser.pugx.org/leafs/fetch/downloads)](https://packagist.org/packages/leafs/fetch) | HTTP requests made simple |
+| [form](/modules/forms/) | [![Latest Stable Version](https://poser.pugx.org/leafs/form/v/stable)](https://packagist.org/packages/leafs/form) [![Total Downloads](https://poser.pugx.org/leafs/form/downloads)](https://packagist.org/packages/leafs/form) | Form processes and validation |
+| [fs](/modules/fs/) | [![Latest Stable Version](https://poser.pugx.org/leafs/fs/v/stable)](https://packagist.org/packages/leafs/fs) [![Total Downloads](https://poser.pugx.org/leafs/fs/downloads)](https://packagist.org/packages/leafs/fs) | Awesome filesystem operations + file uploads |
+| [http](/modules/http/) | [![Latest Stable Version](https://poser.pugx.org/leafs/http/v/stable)](https://packagist.org/packages/leafs/http) [![Total Downloads](https://poser.pugx.org/leafs/http/downloads)](https://packagist.org/packages/leafs/http) | Http operations made simple (request, response, ...) |
+| [inertia](/modules/views/inertia/) | [![Latest Stable Version](https://poser.pugx.org/leafs/inertia/v/stable)](https://packagist.org/packages/leafs/inertia) [![Total Downloads](https://poser.pugx.org/leafs/inertia/downloads)](https://packagist.org/packages/leafs/inertia) | Leaf adapter for inertia JS |
+| [logger](/modules/logger/) | [![Latest Stable Version](https://poser.pugx.org/leafs/logger/v/stable)](https://packagist.org/packages/leafs/logger) [![Total Downloads](https://poser.pugx.org/leafs/logger/downloads)](https://packagist.org/packages/leafs/logger) | leaf logger module |
+| [mail](/modules/mail/) | [![Latest Stable Version](https://poser.pugx.org/leafs/mail/v/stable)](https://packagist.org/packages/leafs/mail) [![Total Downloads](https://poser.pugx.org/leafs/mail/downloads)](https://packagist.org/packages/leafs/mail) | Mailing made easy with leaf |
+| [mvc-core](/modules/mvc-core/) | [![Latest Stable Version](https://poser.pugx.org/leafs/mvc-core/v/stable)](https://packagist.org/packages/leafs/mvc-core) [![Total Downloads](https://poser.pugx.org/leafs/mvc-core/downloads)](https://packagist.org/packages/leafs/mvc-core) | Core MVC tools powering our MVC wrappers |
+| [password](/modules/password/) | [![Latest Stable Version](https://poser.pugx.org/leafs/password/v/stable)](https://packagist.org/packages/leafs/password) [![Total Downloads](https://poser.pugx.org/leafs/password/downloads)](https://packagist.org/packages/leafs/password) | Password encryption/validation/hashing in one box |
+| [redis](/modules/redis/) | [![Latest Stable Version](https://poser.pugx.org/leafs/redis/v/stable)](https://packagist.org/packages/leafs/redis) [![Total Downloads](https://poser.pugx.org/leafs/redis/downloads)](https://packagist.org/packages/leafs/redis) | Redis module |
+| [router](/docs/routing/) | [![Latest Stable Version](https://poser.pugx.org/leafs/router/v/stable)](https://packagist.org/packages/leafs/router) [![Total Downloads](https://poser.pugx.org/leafs/router/downloads)](https://packagist.org/packages/leafs/router) | Default router for leaf php |
+| [session](/modules/session/) | [![Latest Stable Version](https://poser.pugx.org/leafs/session/v/stable)](https://packagist.org/packages/leafs/session) [![Total Downloads](https://poser.pugx.org/leafs/session/downloads)](https://packagist.org/packages/leafs/session) | PHP sessions made simple |
+| [tilly (WIP)](https://archive.leafphp.dev/#/tilly/) | [![Latest Stable Version](https://poser.pugx.org/leafs/tilly/v/stable)](https://packagist.org/packages/leafs/tilly) [![Total Downloads](https://poser.pugx.org/leafs/tilly/downloads)](https://packagist.org/packages/leafs/tilly) | Simple utility 'toolkit' for PHP applications |
+| [veins](/modules/views/veins/) | [![Latest Stable Version](https://poser.pugx.org/leafs/veins/v/stable)](https://packagist.org/packages/leafs/veins) [![Total Downloads](https://poser.pugx.org/leafs/veins/downloads)](https://packagist.org/packages/leafs/veins) | Leaf veins templating engine |
+| [viewi](/modules/views/viewi/) | [![Latest Stable Version](https://poser.pugx.org/leafs/viewi/v/stable)](https://packagist.org/packages/leafs/viewi) [![Total Downloads](https://poser.pugx.org/leafs/viewi/downloads)](https://packagist.org/packages/leafs/viewi) | Leaf integration with Viewi PHP |
+| [vite](/modules/views/vite/) | [![Latest Stable Version](https://poser.pugx.org/leafs/vite/v/stable)](https://packagist.org/packages/leafs/vite) [![Total Downloads](https://poser.pugx.org/leafs/vite/downloads)](https://packagist.org/packages/leafs/vite) | Leaf server component for Vite |
+
+
diff --git a/apps/docs/src/modules/logger/index.md b/apps/docs/src/modules/logger/index.md
new file mode 100644
index 0000000..6d21e7a
--- /dev/null
+++ b/apps/docs/src/modules/logger/index.md
@@ -0,0 +1,63 @@
+# Logger
+
+
+Leaf now provides very well defined logging support for your apps. A sample log looks like this:
+
+```log
+[2021-03-31 22:44:53]
+ERROR - ErrorException: Trying to access array offset on value of type int in /home/mychi/Projects/leafphp/leaf/src/Experimental/Cache.php:83
+Stack trace:
+#0 /home/mychi/Projects/leafphp/leaf/src/Experimental/Cache.php(83): Leaf\Exception\General::handleErrors()
+#1 /home/mychi/Projects/leafphp/leaf/test/index.php(45): Leaf\Experimental\Cache::get()
+#2 [internal function]: {closure}()
+#3 /home/mychi/Projects/leafphp/leaf/src/Router.php(337): call_user_func_array()
+#4 /home/mychi/Projects/leafphp/leaf/src/Router.php(392): Leaf\Router::invoke()
+#5 /home/mychi/Projects/leafphp/leaf/src/Router.php(443): Leaf\Router::handle()
+#6 /home/mychi/Projects/leafphp/leaf/src/App.php(863): Leaf\Router::run()
+#7 /home/mychi/Projects/leafphp/leaf/test/index.php(52): Leaf\App->run()
+#8 {main}
+```
+
+A Leaf provides a log object that writes data to a specific output. The actual writing of data is delegated to a log writer.
+
+**You need to configure the log directory so that Leaf knows where to place your files.**
+
+```php
+ __DIR__ . "/logs/",
+]);
+```
+
+or
+
+```php
+logger();
+```
+
+The log object provides the following PSR-3 interface
+
+```php
+$app->logger()->debug(mixed $object);
+$app->logger()->info(mixed $object);
+$app->logger()->notice(mixed $object);
+$app->logger()->warning(mixed $object);
+$app->logger()->error(mixed $object);
+$app->logger()->critical(mixed $object);
+$app->logger()->alert(mixed $object);
+$app->logger()->emergency(mixed $object);
+```
+
+Each log object method accepts one mixed argument. The argument is usually a string, but the argument can be anything. The log object will pass the argument to its log writer. It is the log writer’s responsibility to write arbitrary input to the appropriate destination.
diff --git a/apps/docs/src/modules/mail/index.md b/apps/docs/src/modules/mail/index.md
new file mode 100755
index 0000000..b27092f
--- /dev/null
+++ b/apps/docs/src/modules/mail/index.md
@@ -0,0 +1,274 @@
+# Leaf Mail
+
+
+
+Mailing in PHP apps has always been seen as a daunting task. Leaf Mail provides a simple, straightforward and efficient email API that is built on the widely used [PHPMailer Library](https://github.com/PHPMailer/PHPMailer) component.
+
+With Leaf Mail, you can easily send emails using various drivers and services such as SMTP, Mailgun, SendGrid, Amazon SES, and sendmail. This flexibility enables you to swiftly begin sending emails through a preferred local or cloud-based service.
+
+## Installation
+
+You can install leaf mail using the leaf cli:
+
+```bash
+leaf install mail
+```
+
+or with composer:
+
+```bash
+composer require leafs/mail
+```
+
+## Setting Up
+
+Leaf Mail provides a `Mailer` class that is responsible for validating and sending emails. This class handles the connection to your mail server, the configuration for how to send your emails and the actual sending of emails.
+
+
+
+It also provides a `Mail` class that is responsible for creating and formatting emails. Most of the time, you'll be using the `Mail` class to create and send emails.
+
+
+
+
+It also provides a `mailer()` method that is responsible for creating and formatting emails. Most of the time, you'll be using the `mailer()` method to create and send emails.
+
+
+
+***Note that you need to setup the connection to your mail server using the `Leaf\Mail\Mailer` class before sending your emails.***
+
+## Mail Server Connection
+
+The `Leaf\Mail\Mailer` class is responsible for connecting to your mail server and handling the sending emails. It provides a `connect()` method that you can use to connect to your mail server.
+
+The `connect()` method takes in an array of configuration options that you can use to configure your mail server connection. The configuration options are:
+
+| Param | Use case |
+| -------- | -------------------------------------------- |
+| host | Hostname for your mail server |
+| port | Port for your mail server |
+| security | Any encryption supported by PHPMailer |
+| auth | Auth for your mail server |
+
+### Gmail connection example
+
+Gmail is one of the most popular mail servers. Unfortunately, a connection with your email and password is no longer supported, so you will need to use OAuth. You will need to add an OAuth provider like [league/oauth2-google](https://github.com/thephpleague/oauth2-google) to your project.
+
+```bash
+leaf install league/oauth2-google
+
+# or with composer
+
+composer require league/oauth2-google
+```
+
+From there you can create your connection like this:
+
+```php
+use Leaf\Mail\Mailer;
+use League\OAuth2\Client\Provider\Google;
+use PHPMailer\PHPMailer\OAuth;
+use PHPMailer\PHPMailer\PHPMailer;
+
+...
+
+Mailer::connect([
+ 'host' => 'smtp.gmail.com',
+ 'port' => 465,
+ 'security' => PHPMailer::ENCRYPTION_SMTPS,
+ 'auth' => new OAuth(
+ [
+ 'userName' => 'mail@gmail.com',
+ 'clientSecret' => 'CLIENT_SECRET',
+ 'clientId' => 'CLIENT_ID',
+ 'refreshToken' => 'GMAIL_REFRESH_TOKEN',
+ 'provider' => new Google(
+ [
+ 'clientId' => 'CLIENT_ID',
+ 'clientSecret' => 'CLIENT_SECRET',
+ ]
+ ),
+ ]
+ )
+]);
+```
+
+### SMTP connection example
+
+The example above uses OAuth, however, some mail servers also support using a username/password for connections. Here's an example of connecting to Mailtrap using SMTP:
+
+```php
+use Leaf\Mail\Mailer;
+use PHPMailer\PHPMailer\PHPMailer;
+
+...
+
+Mailer::connect([
+ 'host' => 'smtp.mailtrap.io',
+ 'port' => 2525,
+ 'security' => PHPMailer::ENCRYPTION_STARTTLS,
+ 'auth' => [
+ 'username' => 'MAILTRAP_USERNAME',
+ 'password' => 'MAILTRAP_PASSWORD'
+ ]
+]);
+```
+
+## Mailer config
+
+The `Mailer` class provides a `config()` method that you can use to configure your mail server connection. The configuration options are:
+
+| Param | Use case |
+| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| debug | Enable or disable debug mode. Supported values are 'SERVER', `false` or any value supported by PHPMailer's `SMTPDebug` config |
+| defaults | This config is used to set default values for the `recipientEmail`, `recipientName`, `senderEmail`, `senderName`, `replyToName`, and `replyToEmail` of your emails. |
+| keepAlive | This config is used to keep the connection to your mail server alive. This is useful if you are sending multiple emails. It takes in a boolean. |
+
+```php
+Mailer::config([
+ 'keepAlive' => true,
+ 'debug' => 'SERVER',
+ 'defaults' => [
+ 'recipientEmail' => 'name@mail.com',
+ 'recipientName' => 'First Last',
+ 'senderName' => 'Leaf Mail',
+ 'senderEmail' => 'mychi@leafphp.dev',
+ ],
+]);
+```
+
+*Setting your defaults allows you to send your mails without having to configure sender/receiver mails for every mail.*
+
+## Your first mail
+
+Now that we've gotten all the annoying config out of the way, all that's left is the easy part: sending your mails.
+
+
+
+To send your first mail, you'll need to create a new instance of the `Leaf\Mail` class. It takes in an array used to create your email:
+
+```php
+$mail = new \Leaf\Mail([
+ 'subject' => 'Leaf Mail Test',
+ 'body' => 'This is a test mail from Leaf Mail using gmail',
+
+ // next couple of lines can be skipped if you
+ // set defaults in the Mailer config
+ 'recipientEmail' => 'name@mail.com',
+ 'recipientName' => 'First Last',
+ 'senderName' => 'Leaf Mail',
+ 'senderEmail' => 'mychi@leafphp.dev',
+]);
+
+// Send your mail
+$mail->send();
+```
+
+You can also use the `create()` method to create your mail:
+
+```php
+$mail = \Leaf\Mail::create([
+ 'subject' => 'Leaf Mail Test',
+ 'body' => 'This is a test mail from Leaf Mail using gmail',
+
+ // next couple of lines can be skipped if you
+ // set defaults in the Mailer config
+ 'recipientEmail' => 'name@mail.com',
+ 'recipientName' => 'First Last',
+ 'senderName' => 'Leaf Mail',
+ 'senderEmail' => 'mychi@leafphp.dev',
+]);
+
+// Send your mail
+$mail->send();
+```
+
+
+
+
+To send your first mail, you'll need to call the `create()` method returned from `mailer()`. It takes in an array used to create your email:
+
+```php
+mailer()
+ ->create([
+ 'subject' => 'Leaf Mail Test',
+ 'body' => 'This is a test mail from Leaf Mail using gmail',
+
+ // next couple of lines can be skipped if you
+ // set defaults in the Mailer config
+ 'recipientEmail' => 'name@mail.com',
+ 'recipientName' => 'First Last',
+ 'senderName' => 'Leaf Mail',
+ 'senderEmail' => 'mychi@leafphp.dev',
+ ])
+ ->send();
+```
+
+
+
+This is a full list of the parameters you can use to create your mail:
+
+| Param | Use case |
+| :-------------- | :--------------------------------------------------------------------------------------------- |
+| subject | The subject of your email |
+| body | The body of your email |
+| recipientEmail | The email of the person you're sending the mail to |
+| recipientName | The name of the person you're sending the mail to |
+| senderName | The name of the person sending the mail |
+| senderEmail | The email of the person sending the mail |
+| replyToName | Add a name for your "Reply-To" address |
+| replyToEmail | Add a "Reply-To" address |
+| cc | The email of the person you want to carbon copy |
+| bcc | The email of the person you want to blank carbon copy |
+| isHTML | A boolean value that determines if your mail is HTML or not |
+| altBody | This body can be read by mail clients that do not have HTML email capability such as mutt & Eudora. Clients that can read HTML will view the normal Body |
+
+## Adding Attachments
+
+You can add attachments to your mail using the `attach()` method. It takes in an array of attachment data or just a string containing a single attachment. The attachment data is an array containing the following keys:
+
+
+
+```php
+$mail = new \Leaf\Mail([
+ 'subject' => 'Leaf Mail Test',
+ 'body' => 'This is a test mail from Leaf Mail using gmail',
+]);
+
+$mail->attach('./attachment.txt');
+$mail->attach([
+ './file1.txt',
+ './file2.txt'
+]);
+
+$mail->send();
+```
+
+
+
+
+```php
+mailer()
+ ->create([
+ 'subject' => 'Leaf Mail Test',
+ 'body' => 'This is a test mail from Leaf Mail using gmail',
+ ])
+ ->attach('./attachment.txt')
+ ->attach([
+ './file1.txt',
+ './file2.txt'
+ ])
+ ->send();
+```
+
+
+
+## Error handling
+
+In order not to flood your application with logs and errors, Leaf Mail gathers all errors thrown by the mail server, and saves them internally. You can return all errors with `$mail->errors()`
+
+```php
+if (!$mail->send(...)) {
+ return $mail->errors();
+}
+```
diff --git a/apps/docs/src/modules/mvc-core/api-controller.md b/apps/docs/src/modules/mvc-core/api-controller.md
new file mode 100755
index 0000000..37e2323
--- /dev/null
+++ b/apps/docs/src/modules/mvc-core/api-controller.md
@@ -0,0 +1,100 @@
+
+# Core API Controller
+
+Instead of defining all of your request handling logic as Closures in route files, you may wish to organize this behavior using Controller classes. Controllers can group related request handling logic into a single class. This particular base controller is made specially for APIs, it's been stripped of anything that would not be used in an API.
+
+## Defining API Controllers
+
+Below is an example of a basic API controller class. Note that the controller extends the base controller class included with Leaf(`Leaf\APIController`). The base class provides a few convenience methods
+
+
+```php
+respond($user);
+ }
+}
+```
+
+You can define a route to this controller action like so:
+
+```php
+$app->get('user/{id}', 'UserController@show');
+```
+
+Now, when a request matches the specified route URI, the `show` method on the `UserController` class will be executed. The route parameters will also be passed to the method.
+
+> Controllers are **not** required to extend a base class. However, you will not have access to convenient features provided by Leaf Model
+
+
+## Base Controller Features
+
+### Responses
+
+Leaf Core controller contains methods to appropriately return data to the user.
+
+**respond:**
+
+```php
+use Leaf\Controller;
+
+class NameController extends Controller {
+ public function index() {
+ $this->respond([
+ 'message' => 'hello'
+ ]);
+ }
+}
+```
+
+You can view more on responses [here](/modules/http/v/2/response)
+
+### file_upload
+
+file_upload is for simple file uploads. It takes in 3 parameters, the path to save the file, the file and the file type(optional). It returns an array `[true, $filename]` if successful and `[false, $error]` if the upload fails.
+
+```php
+use Leaf\Controller;
+
+class NameController extends Controller {
+ public function index() {
+ $profilePic = $_FILES['profile_pic'];
+ // file upload
+ $this->file_upload('./images/', $profilePic);
+ // file upload with file type
+ $this->file_upload('./images/', $profilePic, 'image');
+ }
+}
+```
+
+### Forms
+
+The base controller also gives you a simple way to handle form data
+
+```php
+public function index() {
+ $name = $this->form->get('name');
+
+ $this->validate([
+ 'name' => 'text'
+ ]);
+}
+```
+
+Read more on Leaf Forms [here](/modules/forms/)
diff --git a/apps/docs/src/modules/mvc-core/controller.md b/apps/docs/src/modules/mvc-core/controller.md
new file mode 100755
index 0000000..534cd05
--- /dev/null
+++ b/apps/docs/src/modules/mvc-core/controller.md
@@ -0,0 +1,129 @@
+
+# Core Controller
+
+Instead of defining all of your request handling logic as Closures in route files, you may wish to organize this behavior using Controller classes. Controllers can group related request handling logic into a single class.
+
+## Defining Controllers
+
+Below is an example of a basic controller class. Note that the controller extends the base controller class included with Leaf(`Leaf\Controller`). The base class provides a few convenience methods
+
+
+```php
+set('user', User::findOrFail($id));
+ $this->render('profile');
+ }
+}
+```
+
+You can define a route to this controller action like so:
+
+```php
+$app->get('user/{id}', 'UserController@show');
+```
+
+Now, when a request matches the specified route URI, the `show` method on the `UserController` class will be executed. The route parameters will also be passed to the method.
+
+> Controllers are **not** required to extend a base class. However, you will not have access to convenient features provided by Leaf Model
+
+
+## Base Controller Features
+
+### Default Templating
+
+Using the core controller, you already have access to templating with [Leaf Veins](/modules/views/veins/). You can simply configure and render your template whenever you want to.
+
+```php
+use Leaf\Controller;
+
+class NameController extends Controller {
+ // configure as soon as controller is invoked
+ public function __construct() {
+ $this->veins->configure([
+ 'veins_dir' => 'app/views/',
+ 'cache_dir' => 'app/views/build/'
+ ]);
+ }
+
+ public function index() {
+ // set template data
+ $this->set([
+ 'name' => 'Mychi'
+ ]);
+ // render your template
+ $this->render('index'); // refers to index.vein in the veins_dir
+ }
+}
+```
+
+You can view more info on Veins [here](/modules/views/veins/)
+
+### Responses
+
+Leaf Core controller contains methods to appropriately return data to the user.
+
+**respond:**
+
+```php
+use Leaf\Controller;
+
+class NameController extends Controller {
+ public function index() {
+ $this->respond([
+ 'message' => 'hello'
+ ]);
+ }
+}
+```
+
+You can view more on responses [here](/modules/http/v/2/response)
+
+### file_upload
+
+file_upload is for simple file uploads. It takes in 3 parameters, the path to save the file, the file and the file type(optional). It returns an array `[true, $filename]` if successful and `[false, $error]` if the upload fails.
+
+```php
+use Leaf\Controller;
+
+class NameController extends Controller {
+ public function index() {
+ $profilePic = $_FILES['profile_pic'];
+ // file upload
+ $this->file_upload('./images/', $profilePic);
+ // file upload with file type
+ $this->file_upload('./images/', $profilePic, 'image');
+ }
+}
+```
+
+### Forms
+
+The base controller also gives you a simple way to handle form data
+
+```php
+public function index() {
+ $name = $this->form->get('name');
+
+ $this->validate([
+ 'name' => 'text'
+ ]);
+}
+```
+
+Read more on Leaf Forms [here](/modules/forms/)
diff --git a/apps/docs/src/modules/mvc-core/factories.md b/apps/docs/src/modules/mvc-core/factories.md
new file mode 100755
index 0000000..b062a1d
--- /dev/null
+++ b/apps/docs/src/modules/mvc-core/factories.md
@@ -0,0 +1,91 @@
+
+# Core Factory
+
+## Overview
+
+Instead of defining 100 dummy rows in your database 1 by 1, you can quickly populate your database with dummy but structured data using Factories.
+
+This version of Leaf comes with a nice integration with your models that allows you to create all the data you need in just a few lines of code.
+
+## Working with factories
+
+Just like with other Leaf modules, we try to make working with factories as simple as possible. A regular factory file looks something like this:
+
+```php
+ strtolower($this->faker->firstName),
+ 'name' => $this->faker->name,
+ 'email' => $this->faker->unique()->safeEmail,
+ 'email_verified_at' => \Leaf\Date::now(),
+ 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
+ 'remember_token' => 'random string',
+ ];
+ }
+}
+
+```
+
+As you can see, in their most basic form, factories are classes that extend Leaf's base factory class and define a `model` property and `definition` method. The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory.
+
+As a side note, if the `model` property isn't defined, Leaf tries to determine the model class name from the factory name, if however, that doesn't work, an error would be thrown, and the seeding process halted.
+
+Via the `faker` property, factories have access to the [Faker PHP library](https://github.com/FakerPHP/Faker), which allows you to conveniently generate various kinds of random data for testing.
+
+## Running your factories
+
+Factories are used in your seeds, so to use a factory, head over to it's corresponding Seed, eg: `UserFactory` would be used by the `UsersSeeder`. From there, you just need to initialize the Factory in the `run` method of your seeder. This is in 3 simple parts:
+
+```php
+(new UserFactory)->create(20)->save();
+```
+
+The `(new UserFactory)` part initializes the factory, `create()` takes in a number which defines how many dummy records to generate, finally `save()` instantiates model instances and persists them to the database using Eloquent's save method:
+
+```php
+// Create a single App\Models\User instance...
+(new UserFactory)->save();
+
+// Create three App\Models\User instances...
+(new UserFactory)->create(3)->save();
+```
+
+To see the results of this, you just need to seed your database with the `db:seed` command.
+
+You may override the factory's default model attributes by passing an array of attributes to the create method:
+
+```php
+(new UserFactory)->save([
+ 'name' => 'Mychi',
+]);
+```
+
+**Note that if you're creating multiple records, they'll all have the same data passed into `save`, so it's not recommended to override save when using multiple records.**
+
+In some cases, for whatever reason, you may want to return the users generated instead of saving them in the database directly. You can do this with the `get` method:
+
+```php
+$randomUsers = (new UserFactory)->create(3)->get();
+```
diff --git a/apps/docs/src/modules/mvc-core/index.md b/apps/docs/src/modules/mvc-core/index.md
new file mode 100644
index 0000000..3daff7c
--- /dev/null
+++ b/apps/docs/src/modules/mvc-core/index.md
@@ -0,0 +1,246 @@
+# MVC Core
+
+MVC core is a module that contains components for transforming leaf into a full-blown MVC framework. MVC Core is used in Leaf MVC and Leaf API. It comes with features like controllers, models, eloquent from laravel, factories and more.
+
+## Installation
+
+You can quickly install MVC core using composer or leaf cli.
+
+```bash
+composer require leafs/mvc-core
+```
+
+Or with leaf CLI:
+
+```bash
+leaf install mvc-core
+```
+
+## Config
+
+Leaf MVC and Leaf API are already configured out of the box. If you are using any of these setups, you can skip to the documentation for the component you need.
+
+## Autoloader
+
+MVC core comes with an autoloader that uses the class names and namespaces of classes you use in your code to dynamically load classes. This saves you the trouble of having to require every file you create in your application. This is done for you by default and there's no need to initialize any class or function.
+
+## Controllers
+
+Leaf MVC Core comes in with two (2) controller classes. `Leaf\Controller` for creating controllers in web projects and `Leaf\ApiController` for creating controllers geared towards API development.
+
+## Models
+
+Leaf MVC Core comes with a base model from which all models in your leaf API and leaf MVC apps are created. This model is directly built unto [laravel's eloquent](https://laravel.com/docs/10.x/eloquent) and so we'll recommend checking out laravel models for detailed documentation. Models in your app will look something like this:
+
+```php
+ strtolower($this->faker->firstName),
+ 'name' => $this->faker->name,
+ 'email' => $this->faker->unique()->safeEmail,
+ 'email_verified_at' => \Leaf\Date::now(),
+ 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
+ 'remember_token' => Str::random(10),
+ ];
+ }
+}
+```
+
+The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory.
+
+As a side note, if the `model` property isn't defined, Leaf tries to determine the model class name from the factory name, if however, that doesn't work, an error would be thrown, and the seeding process halted.
+
+Via the `faker` property, factories have access to the [Faker PHP library](https://github.com/FakerPHP/Faker), which allows you to conveniently generate various kinds of random data for testing.
+
+### Running your factories
+
+Factories are used in your seeds, so to use a factory, head over to it's corresponding Seed, eg: `UserFactory` would be used by the `UsersSeeder`. From there, you just need to initialize the Factory in the `run` method of your seeder. This is in 3 simple parts:
+
+```php
+(new UserFactory)->create(20)->save();
+```
+
+The `(new UserFactory)` part initializes the factory, `create()` takes in a number which defines how many dummy records to generate, finally `save()` instantiates model instances and persists them to the database using Eloquent's save method:
+
+```php
+// Create a single App\Models\User instance...
+(new UserFactory)->save();
+
+// Create three App\Models\User instances...
+(new UserFactory)->create(3)->save();
+```
+
+To see the results of this, you just need to seed your database with the `db:seed` command.
+
+You may override the factory's default model attributes by passing an array of attributes to the create method:
+
+```php
+(new UserFactory)->save([
+ 'name' => 'Mychi',
+]);
+```
+
+**Note that if you're creating multiple records, they'll all have the same data passed into `save`, so it's not recommended to override save when using multiple records.**
+
+In some cases, for whatever reason, you may want to return the users generated instead of saving them in the database directly. You can do this with the `get` method:
+
+```php
+$randomUsers = (new UserFactory)->create(3)->get();
+```
+
+## Schema
+
+Schema is a new way to create database migrations. Typing code for migrations the regular way is quite annoying: thinking of the types of data, setting default values and other items. `Schema` allows you to create migration schemas based on example JSON output.
+
+```json
+{
+ "id": 1,
+ "username?": "mychi.darko",
+ "name": "Mychi Darko",
+ "email?": "mychi@leafphp.dev",
+ "email_verified_at?": "2021-07-23T16:18:35.947712157Z",
+ "password": "poekojdenwjwiojweojojweoijoewoj",
+ "remember_token?": "deiwoj",
+ "timestamps": ""
+}
+```
+
+This will generate a migration equivalent to this:
+
+```php
+$table->increments('id');
+$table->string('username')->nullable();
+$table->string('name');
+$table->string('email')->nullable();
+$table->timestamp('email_verified_at')->nullable();
+$table->string('password');
+$table->string('remember_token')->nullable();
+$table->timestamps();
+```
+
+Schema takes care of all the work involved and generates a migration based on sample data. This means that if you have an idea of what your database should look like, leaf will take care of the rest.
+
+### Using leaf schema
+
+To get started with leaf schema, simply call `Leaf\Schema::build` in your migration. It takes in the capsule to build your migrations with and the json schema to build.
+
+Using the example above (users.json):
+
+```json
+{
+ "id": 1,
+ "username?": "mychi.darko",
+ "name": "Mychi Darko",
+ "email?": "mychi@leafphp.dev",
+ "email_verified_at?": "2021-07-23T16:18:35.947712157Z",
+ "password": "poekojdenwjwiojweojojweoijoewoj",
+ "remember_token?": "deiwoj",
+ "timestamps": ""
+}
+```
+
+user_migration.php
+
+```php
+...
+use Leaf\Schema;
+
+class CreateUsers extends Database {
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up() {
+ Schema::build("users");
+ }
+ ...
+```
+
+In this case leaf will generate a migration to the users table since our filename is `users.json`. Note that leaf schema will always use the filename as the table name.
+
+## Globals
+
+Leaf MVC core comes with the most global functions in all of leaf. This includes functions for configurations, paths and other essentials in MVC applications.
+
+- **AppPaths**: This is a function which holds all of the paths in your application. This is usually used by leaf, as you will be using some of the functions below.
+
+```php
+$paths = AppPaths();
+```
+
+- **ConfigPath**: This global returns the path to your configuration folder in Leaf MVC and Leaf API.
+
+```php
+$dbConfigFile = ConfigPath("db.php");
+// -> Config/db.php
+```
+
+- **CommandsPath**: This returns the path to your commands defined.
+
+```php
+$command = CommandsPath("MainCommand.php");
+// -> App/Commands/MainCommand.php
+```
+
+- **ControllersPath**: Path to your controllers
+
+- **DatabasePath**: Path to your database files (migrations, schema, factories...)
+
+- **FactoriesPath**: Path to your model factories
+
+- **HelpersPath**: Path to your helpers
+
+- **LibPath**: Path to your lib folder if it exists.
+
+- **MigrationsPath**: Path to your mirations
+
+- **ModelsPath**: Path to your models
+
+- **RoutesPath**: Path to your routes
+
+- **SeedsPath**: Path to your seed files
+
+- **StoragePath**: Path to storage directory
+
+- **ViewsPath**: Path to your view files
diff --git a/apps/docs/src/modules/mvc-core/model.md b/apps/docs/src/modules/mvc-core/model.md
new file mode 100755
index 0000000..d8d9a5e
--- /dev/null
+++ b/apps/docs/src/modules/mvc-core/model.md
@@ -0,0 +1,277 @@
+
+# 📕 Core Model
+
+## Overview
+
+Leaf has provided a very simple core model for use in your applications. This core model is powered by the `Leaf ORM` and so all it's features are available for use in Leaf.
+
+This **model** provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table.
+
+## Defining Models
+
+To get started, let's create a simple model. If you are not using a Leaf framework like [Leaf MVC](https://leafmvc.netlify.com) or [Leaf API](https://github.com/leafsphp/leafAPI), you can create directory to hold all your application models. All your models should extend `Leaf\Model` class.
+
+Before getting started, be sure to configure a database connection with a `.env` file. You can take a look at this [example env file](https://gist.github.com/mr-phlames/cbc85b7e4fa8ce5474aea0aec277c7f6).
+
+## Leaf Model Conventions
+
+Now, let's look at an example `Flight` model, which we will use to retrieve and store information from our `flights` database table:
+
+```php
+
+
+## Default Attribute Values
+
+If you would like to define the default values for some of your model's attributes, you may define an $attributes property on your model:
+
+```php
+ false,
+ ];
+}
+```
+
+
+
+## Retrieving Models
+
+Once you have created a model and its associated database table, you are ready to start retrieving data from your database. Think of each Leaf model as a powerful query builder allowing you to fluently query the database table associated with the model. For example:
+
+```php
+name;
+}
+```
+
+### Adding Additional Constraints
+
+The Leaf all method will return all of the results in the model's table. Since each Leaf model serves as a query builder, you may also add constraints to queries, and then use the get method to retrieve the results:
+
+```php
+$flights = Flight::where('active', 1)->orderBy('name', 'desc')->take(10)->get();
+```
+
+> **You can check [here](https://laravel.com/docs/5.8/queries) for available queries on your models.**
+
+### Refreshing Models
+
+You can refresh models using the `fresh` and `refresh` methods. The `fresh` method will re-retrieve the model from the database. The existing model instance will not be affected:
+
+```php
+$flight = Flight::where('number', 'FR 900')->first();
+
+$freshFlight = $flight->fresh();
+```
+
+The `refresh` method will re-hydrate the existing model using fresh data from the database. In addition, all of its loaded relationships will be refreshed as well:
+
+```php
+$flight = Flight::where('number', 'FR 900')->first();
+
+$flight->number = 'FR 456';
+
+$flight->refresh();
+
+$flight->number; // "FR 900"
+```
+
+
+
+## Inserting & Updating Models
+
+### Inserts
+
+To create a new record in the database, create a new model instance, set attributes on the model, then call the save method:
+
+```php
+name = $this->request->name;
+
+ $flight->save();
+ }
+}
+```
+
+In this example, we assign the name parameter from the incoming HTTP request to the name attribute of the `Flight` model instance. When we call the save method, a record will be inserted into the database. The created_at and updated_at timestamps will automatically be set when the save method is called, so there is no need to set them manually.
+
+### Updates
+
+The save method may also be used to update models that already exist in the database. To update a model, you should retrieve it, set any attributes you wish to update, and then call the save method. Again, the updated_at timestamp will automatically be updated, so there is no need to manually set its value:
+
+```php
+$flight = Flight::find(1);
+
+$flight->name = 'New Flight Name';
+
+$flight->save();
+```
+
+
+
+Since Leaf Models use Eloquent, you can read more [here](https://laravel.com/docs/5.8/eloquent) to view available methods on the Eloquent object.
+
+**Just remember, your models should extend `Leaf\Model` not `Eloquent`**
diff --git a/apps/docs/src/modules/password/index.md b/apps/docs/src/modules/password/index.md
new file mode 100755
index 0000000..184bdb1
--- /dev/null
+++ b/apps/docs/src/modules/password/index.md
@@ -0,0 +1,127 @@
+# Leaf Password
+
+
+
+Password encoding and verification are one of the most important parts of any application. This usually involves a lot of security concerns and can be a pain to implement. Leaf makes this process a lot easier with the password helper.
+
+This module simply helps create and manage passwords, encrypt and verify without any security concerns. It is fully static, as such, can be used from anywhere in your application.
+
+## Installation
+
+You can quickly install a password helper through the leaf cli:
+
+```bash
+leaf install password
+```
+
+or you can install it via composer:
+
+```bash
+composer require leafs/password
+```
+
+## spice
+
+Just as the name implies, spice adds a little "spice" to users' passwords. They help make even weak passwords a pain for systems to crack by chaining additional characters to the password before encoding or decoding.
+
+A weak password like `password123` when spiced can become `@X$p0#f&password123` without pressing the user to stick to "Your password should contain numbers, letters and ...".
+
+The `spice` method can both be used to set and get the password spice.
+
+This sets the password spice which will be encrypted based on the hash you set:
+
+```php
+use Leaf\Helpers\Password;
+
+Password::spice('#@%7g0!&');
+```
+
+**The next examples will assume you've added `use Leaf\Helpers\Password`**
+
+This returns the password spice:
+
+```php
+$spice = Password::spice();
+```
+
+**Spices are automaticatically chained to all password related stuff, so after setting your spice, you don't need to worry about it.**
+
+## `Password::hash()`
+
+This method basically creates a password hash. It takes in 3 parameters:
+
+- The password to encrypt
+- The encryption hash (optional)
+- An array of options for the password hash (optional)
+
+```php
+$hash = Password::hash('USER_PASSWORD', Password::BCRYPT);
+```
+
+The default encryption hash used if none is provided is `Password::DEFAULT` which is `PASSWORD_DEFAULT`.
+
+Also, the most commonly used hashes, BCRYPT and Argon2 are accessible on the Password Helper object as `Password::BCRYPT` and `Password::ARGON2`.
+
+The final options array differs based on the hash you're using. See the [password algorithm constants](https://secure.php.net/manual/en/password.constants.php) for documentation on the supported options for each algorithm.
+
+## `Password::verify()`
+
+Verifying a user’s password has been made really simple thanks to the `verify()` method. Simply pass the plaintext password supplied by the user and compare it to the stored hash, like so:
+
+```php
+if (Password::verify($password, $hashedPassword)) {
+ // handle user login here
+}
+```
+
+verify returns true on success and false on failure.
+
+`$hashedPassword` in the following examples refers to the stored hashed password.
+
+## argon 2
+
+Argon2 is one encryption method heavily used by a lot of developers. Although creating and verifying passwords with argon2 is nothing difficult, Leaf makes it even simpler with methods targetting only argon.
+
+### `argon2()`
+
+This is a simply method used to create an Argon2 hash for your password. It takes in 2 parameters, the password to encrypt and the options for the hashing.
+
+```php
+$hash = Password::argon2($password, $options);
+```
+
+The options parameter is optional, but in case you want to set your own options, see the [password algorithm constants](https://secure.php.net/manual/en/password.constants.php) for documentation on the supported options for Argon2.
+
+### `argon2Verify()`
+
+This method simply checks the validity of an Argon2 hash.
+
+```php
+if (Password::argon2Verify($password, $hashedPassword)) {
+ // handle user login here
+}
+```
+
+## BCRYPT
+
+BCRYPT is another hash used widely by a lot of developers, especially since support with BCRYPT has been on longer than other hashes like Argon 2. We just make hashing with BCRYPT even easier than it currently is.
+
+### `bcrypt()`
+
+This is a simply method used to create an BCRYPT hash for your password. It takes in 2 parameters, the password to encrypt and the options for the hashing.
+
+```php
+$hash = Password::bcrypt($password, $options);
+```
+
+The options parameter is optional, but in case you want to set your own options, see the [password algorithm constants](https://secure.php.net/manual/en/password.constants.php) for documentation on the supported options for BCRYPT.
+
+### `brcyptVerify()`
+
+This method simply checks the validity of an BCRYPT hash.
+
+```php
+if (Password::brcyptVerify($password, $hashedPassword)) {
+ // handle user login here
+}
+```
diff --git a/apps/docs/src/modules/queues/index.md b/apps/docs/src/modules/queues/index.md
new file mode 100644
index 0000000..2bb9e3e
--- /dev/null
+++ b/apps/docs/src/modules/queues/index.md
@@ -0,0 +1,217 @@
+# Queues
+
+
+
+
+
+During the development of your web application, there are tasks that can be time-consuming, such as parsing and storing a CSV file that has been uploaded. However, with Leaf, you have the advantage of easily creating queued jobs that can be processed in the background. By offloading these intensive tasks to a queue, your Leaf application can swiftly respond to web requests, resulting in improved speed and a better user experience for your customers.
+
+
+
+## Queues in Leaf
+
+Implementing a queuing system from scratch can be a daunting task, and can take a lot of time. For this reason, Leaf provides a unified API for using queues across a variety of different backends, such as Redis or your database.
+
+
+
+::: warning Compatibility Note
+Queues are only supported by Leaf MVC and Leaf API applications. We plan to add support for Leaf Core in the near future.
+:::
+
+## Installation
+
+To get started with Queues in Leaf, you need to install the `leaf/queue` package:
+
+```bash
+leaf install queue
+```
+
+Or you can install it via composer:
+
+```bash
+composer require leafs/queue
+```
+
+After installing the package, you need to register the Leaf Queue commands in Aloe CLI. You can do this by adding the following line to your `leaf` file in the root of your Leaf MVC or Leaf API application:
+
+```php
+$console->register(\Leaf\Queue::commands());
+```
+
+This should give you access to the following commands:
+
+- `php leaf g:job` - Generate a job class.
+- `php leaf d:job` - Delete a job class.
+- `php leaf queue:config` - Generate a queue configuration file.
+- `php leaf queue:install` - Generate and run a migration file for the queue table.
+- `php leaf queue:run` - Start the queue worker.
+
+## Configuration
+
+After installing the leaf queue package, you need to setup your queue configuration. Leaf provides a unified API for using queues across a variety of different backends, such Redis or your database, with plans to add support for Amazon SQS, BeanStalk, and others in the future. In your Leaf MVC and Leaf API applications, the queue configuration file is located at `config/queue.php`. This file allows you to configure all of your queue connections. By default, this file is not present in your application, so you need to create it using the following command:
+
+```bash
+php leaf queue:config
+```
+
+This will generate a `queue.php` file in your `config` directory. The file contains examples for configuring each queue driver that is supported by Leaf. Make sure to read the comments in the file and configure a queue connection before using the queue API.
+
+## Configuration options
+
+There are several configuration options available to you in the `config/queue.php` configuration file. These options are used to determine the connection information for your queues, as well as various other options such as queue retry settings, queue logging, queue worker sleep durations, and more.
+
+### Adapter
+
+The `adapter` option specifies the system that will be used to run your queues. Leaf supports `redis` and `db` as queue adapters. The `redis` adapter uses Redis as a queue backend, while the `db` adapter uses your database as a queue backend.
+
+### Default
+
+The `default` option specifies which of the queue connections found in your config should be used as the default connection for all queue operations. Leaf supports `redis`, `sqlite`, `mysql`, `pgsql`, and `sqlsrv` as queue connections. You can also specify a custom connection by providing the name of a connection that is defined in the `connections` array of your `config/queue.php` file.
+
+### Connections
+
+The `connections` option contains an array of all of the queue connections defined for your application. Each connection corresponds to a queue adapter supported by Leaf. For example, the following configuration defines a connection named `redis` that uses the `redis` adapter to connect to a Redis server:
+
+```php
+'connections' => [
+ 'redis' => [
+ 'host' => _env('REDIS_HOST', '127.0.0.1'),
+ 'port' => _env('REDIS_PORT', '6379'),
+ 'password' => _env('REDIS_PASSWORD', ''),
+ 'dbname' => _env('REDIS_DB', 0),
+ ],
+
+ ...
+```
+
+### Queue table
+
+If you are using the `db` adapter, you will need to configure a database table to store your jobs. You can use the `table` option to specify the name of the table. By default, Leaf will use the `leafphp_main_jobs` table that is already included with your application. If you would like to use a different table, you should create the table and specify its name in your `config/queue.php` configuration file:
+
+```php
+'table' => 'leafphp_jobs',
+```
+
+### Worker Config
+
+Worker config includes the default settings used by your worker when executing a job. These settings can be specified when dispatching a job, but if not specified, the worker will use these settings instead.
+
+- delay: The number of seconds to wait before processing a job.
+- delayBeforeRetry: The number of seconds to wait before retrying a job that has failed.
+- expire: The number of seconds to wait before archiving a job that has not yet been processed.
+- force: Whether to force the worker to process the job, even if it has expired or has reached its maximum number of retries.
+- memory: The maximum amount of memory the worker may consume.
+- quitOnEmpty: Whether the worker should quit when the queue is empty.
+- sleep: The number of seconds to wait before polling the queue for new jobs.
+- timeout: The number of seconds a child process can run before being killed.
+- tries: The maximum number of times a job may be attempted.
+
+## Connecting to your queue
+
+As mentioned above, Leaf queue only supports `redis` and `db` as queue adapters. To connect to your queue, you need to specify the adapter and connection you want to use. You can do this by specifying the adapter and connection in the `config/queue.php` file:
+
+```php
+'adapter' => 'redis',
+'default' => 'redis',
+```
+
+If you are using the `db` adapter, you will need to configure a database table to store your jobs. Leaf queue comes with a command to generate and run a migration file for the queue table. You can generate the migration file using the following command:
+
+```bash
+php leaf queue:install
+```
+
+Don't forget to change the `default` option in your `config/queue.php` file to the name of the connection you want to use.
+
+After this is done, you can start adding jobs to your queue.
+
+## Creating a job
+
+Jobs are the tasks that you want to run in the background. For example, you may want to send an email to a user after they have registered for your application. Instead of sending the email directly from your controller, you can create a job that sends the email, and then dispatch the job to the queue. This way, the user will not have to wait for the email to be sent before they can continue using your application.
+
+You can create a job using the `g:job` command:
+
+```bash
+php leaf g:job SendEmail
+```
+
+This will generate a `SendEmailJob` class in your `app/Jobs` directory. The class will contain a `handle` method that will be called when the job is processed by the queue. You can add any code you want to this method. For example, if you want to send an email, you can use the custom created `UserMailer` class to send the email:
+
+```php
+send();
+ }
+}
+```
+
+## Dispatching a job
+
+After creating a job, you can dispatch it to the queue using the `dispatch()` method:
+
+```php
+\App\Jobs\SendEmailJob::dispatch();
+```
+
+Some jobs like the send email job above may require some data to be passed to the job. You can pass data to the job using the `with()` method:
+
+```php
+\App\Jobs\SendEmailJob::with($userId)->dispatch();
+```
+
+You can also pass an array of data to the `with()` method:
+
+```php
+\App\Jobs\SendEmailJob::with(['userId' => $userId])->dispatch();
+```
+
+## Specifying options for a job
+
+In the config file, you can specify default options for your jobs. However, you can also specify options for a job when dispatching it to the queue. For example, if you want to delay a job for 5 minutes, you can do so by passing the `delay` option to the `dispatch()` method:
+
+```php
+\App\Jobs\SendEmailJob::with($userId)->dispatch(['delay' => 5]);
+```
+
+The available options are:
+
+- delay: The number of seconds to wait before processing a job.
+- delayBeforeRetry: The number of seconds to wait before retrying a job that has failed.
+- expire: The number of seconds to wait before archiving a job that has not yet been processed.
+- force: Whether to force the worker to process the job, even if it has expired or has reached its maximum number of retries.
+- memory: The maximum amount of memory the worker may consume.
+- timeout: The number of seconds a child process can run before being killed.
+- tries: The maximum number of times a job may be attempted.
+
+## Running the queue worker
+
+After dispatching a job to the queue, you need to run the queue worker to process the job. You can do this by running the following command:
+
+```bash
+php leaf queue:run
+```
+
+This will start the queue worker and process any jobs that have been dispatched to the queue.
+
+
diff --git a/apps/docs/src/modules/redis/index.md b/apps/docs/src/modules/redis/index.md
new file mode 100644
index 0000000..af7b8ae
--- /dev/null
+++ b/apps/docs/src/modules/redis/index.md
@@ -0,0 +1,236 @@
+# Leaf Redis
+
+
+
+
+
+
+According to the [Redis docs](https://redis.io/), Redis is an open source (BSD licensed), in-memory data structure store used as a database, cache, message broker, and streaming engine. Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams. Redis has built-in replication, Lua scripting, LRU eviction, transactions, and different levels of on-disk persistence, and provides high availability via Redis Sentinel and automatic partitioning with Redis Cluster.
+
+In simpler terms, Redis is a fast, open source, in-memory key-value store that can be used as a database, cache, and message broker. As a database, Redis allows you to store and retrieve data quickly by keeping it in memory rather than on disk, which makes it ideal for applications that require high-speed access to data. As a cache, Redis can help speed up web applications by storing frequently accessed data in memory, reducing the number of times the application needs to access a slower database.
+
+Leaf includes a Redis module that allows you to easily integrate Redis into your Leaf application.
+
+
+New to Redis?
+
+We've included some resources to help you get started with Redis.
+
+
+
+
+
+## Installation
+
+You can quickly and simply install Leaf Redis through composer or the leaf cli.
+
+```bash
+composer require leafs/redis
+```
+
+or with the leaf cli:
+
+```bash
+leaf install redis
+```
+
+## Getting Started
+
+To get started with Leaf Redis, you simply need to call the `init` method and pass in any configuration you need.
+
+```php
+Leaf\Redis::init();
+```
+
+This will initialize a new redis connection, from there, you can call any function you need to call.
+
+### Aloe CLI
+
+::: warning Note
+
+You only need to use this section if you're using Leaf MVC or Leaf API.
+
+:::
+
+Although Leaf Redis can be used outside the Leaf environment, there's more support for Leaf based frameworks. Leaf Redis comes with out of the box support for Aloe CLI which is used in Leaf MVC and Leaf API. To get started, head over to the `leaf` file in the root directory of your Leaf API/Leaf MVC app or wherever aloe CLI is registered and register a new command.
+
+```php
+$console->register(\Leaf\Redis::commands());
+```
+
+From there you should have access to a bunch of new commands from Leaf redis. The available commands are:
+
+```bash
+redis
+ redis:install Create leaf redis config and .env variables
+ redis:server Start redis server
+```
+
+From there, you can run `php leaf redis:install` to install the redis config and environment variables. You can then run `php leaf redis:server` to start the redis server.
+
+## Config
+
+As mentioned above, the `init` method takes in an array for configuration. Below is the default config for `init`.
+
+```php
+/*
+|--------------------------------------------------------------------------
+| Redis host
+|--------------------------------------------------------------------------
+|
+| Set the host for redis connection
+|
+*/
+"host" => "127.0.0.1",
+
+/*
+|--------------------------------------------------------------------------
+| Redis host port
+|--------------------------------------------------------------------------
+|
+| Set the port for redis host
+|
+*/
+"port" => 6379,
+
+/*
+|--------------------------------------------------------------------------
+| Redis auth
+|--------------------------------------------------------------------------
+|
+| Set the password for redis connection
+|
+*/
+"password" => null,
+
+/*
+|--------------------------------------------------------------------------
+| Redis session handler
+|--------------------------------------------------------------------------
+|
+| Set redis as session save handler
+|
+*/
+"session" => false,
+
+/*
+|--------------------------------------------------------------------------
+| Redis connection timeout
+|--------------------------------------------------------------------------
+|
+| Value in seconds (optional, default is 0.0 meaning unlimited)
+|
+*/
+"connection.timeout" => 0.0,
+
+/*
+|--------------------------------------------------------------------------
+| Redis connection reserved
+|--------------------------------------------------------------------------
+|
+| should be null if $retryInterval is specified
+|
+*/
+"connection.reserved" => null,
+
+/*
+|--------------------------------------------------------------------------
+| Redis session handler
+|--------------------------------------------------------------------------
+|
+| Connection retry interval in milliseconds.
+|
+*/
+"connection.retryInterval" => 0,
+
+/*
+|--------------------------------------------------------------------------
+| Redis connection read timeout
+|--------------------------------------------------------------------------
+|
+| Value in seconds (optional, default is 0 meaning unlimited
+|
+*/
+"connection.readTimeout" => 0.0,
+
+/*
+|--------------------------------------------------------------------------
+| Redis session save_path
+|--------------------------------------------------------------------------
+|
+| Save path for redis session. Leave null to automatically
+| generate the session save path. You can also use multiple save urls
+| by passing in an array.
+|
+*/
+"session.savePath" => null,
+
+/*
+|--------------------------------------------------------------------------
+| Redis session save_path options
+|--------------------------------------------------------------------------
+|
+| Options for session save path. You can pass in multiple options in
+| the order of the save path above.
+|
+*/
+"session.saveOptions" => [],
+```
+
+```php
+use Leaf\Redis;
+
+Redis::init([
+ // you can use multiple hosts
+ "session.savePath" => ["tcp://host1:6379", "tcp://host2:6379"],
+
+ // the first array is for the first host, second for the second host
+ "session.saveOptions" => [["weight" => 1], ["weight" => 2]],
+]);
+```
+
+## Available Methods
+
+### set
+
+This allows you to set a redis entry.
+
+```php
+Leaf\Redis::set("key", "value");
+
+// you can also use arrays to set multiple values at once
+
+Leaf\Redis::set(["key" => "value", "key2" => "value"]);
+```
+
+### get
+
+This returns a saved redis entry.
+
+```php
+$value = Leaf\Redis::get("key");
+
+// You can also get multiple entries at once
+
+$data = Leaf\Redis::get(["key", "key2"]);
+
+// $data => [key => value, key2 => value]
+```
+
+### ping
+
+Ping the redis server
+
+```php
+Leaf\Redis::ping();
+```
+
diff --git a/apps/docs/src/modules/router/controller.md b/apps/docs/src/modules/router/controller.md
new file mode 100755
index 0000000..13d27eb
--- /dev/null
+++ b/apps/docs/src/modules/router/controller.md
@@ -0,0 +1,124 @@
+---
+title: "Using Controllers"
+---
+
+# Using Controllers
+
+
+Controllers are simply classes that serve as bridges between Models and the View part of your application. Don't think too much of controllers, they're nothing but a class.
+
+In this section, we'll be looking at how to handle a route with a controller. So let's make an example controller: **remember it's just a php class**
+
+```php
+get("/home");
+```
+
+When using controllers, instead of defining a closure or function as the second parameter of your route, you rather pass in a string of the controller's class name and the function you want to use. In this case, `"HomeController@index"`, so remember, it's `Class@Method`
+
+```php
+$app = new Leaf\App;
+
+require "HomeController.php";
+
+$app->get("/home", "HomeController@index");
+```
+
+## Controller Namespaces
+
+In case you're using an auto loader or using leaf in another framework and you have your controllers in another directory, you can do sommething like this
+
+```php
+$app->get('/(\d+)', '\App\Controllers\User@showProfile');
+```
+
+But this gets tedious if you have a lot of routes. So Leaf allows you to set a "general" namespace, you can set the default namespace to use on your router instance via `setNamespace()`
+
+```php
+$app->setNamespace('\App\Controllers');
+
+$app->get('/users/(\d+)', 'User@showProfile');
+$app->get('/cars/(\d+)', 'Car@showProfile');
+```
+
+## Resource Controller
+
+Resource Controllers contain methods to handle CRUD functionality.
+
+```php
+request = new Request;
+ }
+
+ /**
+ * Display a listing of the resource.
+ */
+ public function index() {
+ //
+ }
+
+ /**
+ * Show the form for creating a new resource.
+ */
+ public function create() {
+ //
+ }
+
+ /**
+ * Store a newly created resource in storage.
+ */
+ public function store() {
+ //
+ }
+
+ /**
+ * Display the specified resource.
+ */
+ public function show($id) {
+ //
+ }
+
+ /**
+ * Show the form for editing the specified resource.
+ */
+ public function edit($id) {
+ //
+ }
+
+ /**
+ * Update the specified resource in storage.
+ */
+ public function update($id) {
+ //
+ }
+
+ /**
+ * Remove the specified resource from storage.
+ */
+ public function destroy($id) {
+ //
+ }
+}
+```
diff --git a/apps/docs/src/modules/router/dynamic.md b/apps/docs/src/modules/router/dynamic.md
new file mode 100755
index 0000000..937622c
--- /dev/null
+++ b/apps/docs/src/modules/router/dynamic.md
@@ -0,0 +1,76 @@
+---
+title: "Dynamic Routing"
+---
+
+# Dynamic Routing
+
+
+## Named Params
+
+*This guide assumes you have read [Simple Routing](/modules/router/)*
+
+Basically, Dynamic Placeholder-based Route Patterns are just another way to use routes dynamically. This type of Route Patterns are the same as Dynamic PCRE-based Route Patterns, but with one difference: they don't use regexes to do the pattern matching but they use the more easy placeholders instead. Placeholders are strings surrounded by curly braces, e.g. {name}. You don't need to add parens around placeholders.
+
+Examples
+
+- /movies/{id}
+- /profile/{username}
+
+Placeholders are easier to use than PRCEs, but offer you less control as they internally get translated to a PRCE that matches any character (.*).
+
+```php
+app()->get('/movies/{movieId}/photos/{photoId}', function ($movieId, $photoId) {
+ echo 'Movie #' . $movieId . ', photo #' . $photoId);
+});
+```
+
+**Note:** the name of the placeholder does not need to match with the name of the parameter that is passed into the route handling function...although it's adviced:
+
+```php
+app()->get('/movies/{foo}/photos/{bar}', function ($movieId, $photoId) {
+ echo 'Movie #' . $movieId . ', photo #' . $photoId);
+});
+```
+
+## PCRE Based Params
+
+Basically, PCRE based patterns are just another way to use routes dynamically. This type of Route Patterns contain dynamic parts which can vary per request. The varying parts are named subpatterns and are defined using regular expressions.
+
+Examples
+
+- /movies/(\d+)
+- /profile/(\w+)
+
+Commonly used PCRE-based subpatterns within Dynamic Route Patterns are:
+
+- \d+ = One or more digits (0-9)
+- \w+ = One or more word characters (a-z 0-9 _)
+- [a-z0-9_-]+ = One or more word characters (a-z 0-9 _) and the dash (-)
+- .* = Any character (including /), zero or more
+- [^/]+ = Any character but /, one or more
+
+Note: The PHP PCRE Cheat Sheet might come in handy.
+
+The subpatterns defined in Dynamic PCRE-based Route Patterns are converted to parameters which are passed into the route handling function. Prerequisite is that these subpatterns need to be defined as parenthesized subpatterns, which means that they should be wrapped between parens:
+
+```php
+// Bad
+app()->get('/hello/\w+', function ($name) {
+ echo 'Hello ' . htmlentities($name);
+});
+
+// Good
+app()->get('/hello/(\w+)', function ($name) {
+ echo 'Hello ' . htmlentities($name);
+});
+```
+
+**Note**: The leading `/` at the very beginning of a route pattern is not mandatory, but is recommended.
+
+When multiple subpatterns are defined, the resulting route handling parameters are passed into the route handling function in the order they are defined in:
+
+```php
+app()->get('/movies/(\d+)/photos/(\d+)', function ($movieId, $photoId) {
+ echo 'Movie #' . $movieId . ', photo #' . $photoId);
+});
+```
diff --git a/apps/docs/src/modules/router/errors.md b/apps/docs/src/modules/router/errors.md
new file mode 100755
index 0000000..fb9ca3e
--- /dev/null
+++ b/apps/docs/src/modules/router/errors.md
@@ -0,0 +1,41 @@
+# Error Handling
+
+By default Leaf has error screens which are displayed for application exceptions, 404s and production server errors, however, Leaf also gives you full control and allows you to customize what is shown when an error or exception is encountered.
+
+## Handling 404
+
+Leaf displays a 404 screen for users, however, it may not always be appropriate, especially when you're building an API. You will probably want to return JSON instead of markup. For cases like this, Leaf has prepared a `set404` method on the Leaf instance.
+
+This method allows you to customize what a user sees when they visit a route that doesn't exist in your application. It takes in one parameter, a callable in the form of a function or an array.
+
+The example below displays a custom 404 page.
+
+```php
+Router::set404(function () use($app) {
+ response()->page("./pages/404.html");
+});
+```
+
+## Application Down
+
+Leaf router is also able to dynamically handle placing your application in maintenance mode using the `configure` method.
+
+```php
+Router::configure([
+ "app.down" => true,
+]);
+```
+
+Alternatively, you could also place your application in maintenance mode by setting the `APP_DOWN` environment variable to true. Since `.env` variables are given more priority than router config, the router config will be ignored as long as the env is set.
+
+::: warning Note that
+Leaf router expects you to manually load your `.env` file and will not be responsible for this. You can use [vlucas/phpdotenv](https://packagist.org/packages/vlucas/phpdotenv) to do this. After loading your `.env` variables into your app, leaf router will automatically pick them up.
+:::
+
+Along with this, we have prepared a simple method to display a custom maintenance error page: `setDown`.
+
+```php
+Router::setDown(function () {
+ echo "Down for maintenance";
+});
+```
diff --git a/apps/docs/src/modules/router/index.md b/apps/docs/src/modules/router/index.md
new file mode 100755
index 0000000..4922df7
--- /dev/null
+++ b/apps/docs/src/modules/router/index.md
@@ -0,0 +1,273 @@
+---
+title: "Basic routing"
+---
+
+# Routing
+
+
+::: tip
+Leaf router is now separated from Leaf and is now available as an installable module via composer or the leaf cli.
+:::
+
+Leaf router uses a single root file, to which all the server requests are redirected, it then takes these requests and matches them to rules you have defined. The results are then displayed to the user. It's actually a very simple concept.
+
+::: warning Note
+Leaf router is automatically installed and attached to your leaf apps, so you don't need to manually install it. This guide focuses on leaf router and a general application of it. If you want the leaf routing guide with leaf router, check out [routing](/docs/routing/) instead.
+:::
+
+You can install the leaf router with composer:
+
+```bash
+composer require leafs/router
+```
+
+or with leaf CLI:
+
+```bash
+leaf install router
+```
+
+After this, you can use all of leaf router's functionality with the router class below.
+
+## Router class
+
+The router class is the interface you interact with to perform any routing actions in your app. Leaf core directly integrates with the router class, which means that there is no need to use this class directly, if however, you are using leaf router outside of leaf, you will need to use the router class itself.
+
+```php
+use Leaf\Router;
+
+Router::get("/", "PagesController@index");
+
+Router::run();
+```
+
+## Creating Routes
+
+::: warning IMPORTANT
+From this point onwards, we will assume that you are using Leaf router outside a leaf app, as such, we will use the router class syntax:
+
+```php
+Router::get('/', function () {...});
+```
+
+Check out the [routing guide](/docs/routing/) for an application tailored for leaf apps.
+
+:::
+
+You can define application routes using proxy methods on the Leaf\App instance. Leaf supports different types of requests, let's look at them.
+
+### GET
+
+You can add a route that handles only GET HTTP requests with the Leaf router's get() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+```php
+Router::get('/home', function () {
+ // your code
+});
+```
+
+### POST
+
+You can add a route that handles only POST HTTP requests with the Leaf router's post() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+```php
+Router::post('/users/add', function () use($request) {
+ $user = $request->get('user');
+ // create a new user
+});
+```
+
+Using Post Params
+View [Request](/modules/http/v/2/request) for more info on handling params
+
+### PUT requests
+
+You can add a route that handles only PUT HTTP requests with the Leaf router’s put() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+```php
+Router::put('/book/edit/{id}', function ($id) {
+ // your code
+});
+```
+
+### DELETE requests
+
+You can add a route that handles only DELETE HTTP requests with the Leaf router's delete() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+```php
+Router::delete('/quotes/{id}', function ($id) {
+ // delete quote
+});
+```
+
+### OPTIONS requests
+
+You can add a route that handles only OPTIONS HTTP requests with the Leaf router's options() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+```php
+Router::options('/quotes/{id}', function ($id) {
+ // return headers
+});
+```
+
+### PATCH requests
+
+You can add a route that handles only PATCH HTTP requests with the Leaf router's patch() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+```php
+Router::patch('/post/{id}', function ($id) {
+ // your code
+});
+```
+
+### ALL requests
+
+You can add a route that handles all HTTP requests with the Leaf router's all() method. It accepts two arguments:
+
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+```php
+Router::all('/post/{id}', function ($id) {
+ // your code
+});
+```
+
+### View
+
+**`view` is no longer supported, as Leaf Blade is no longer default in Leaf. You'll have to manually show your views using `get`**
+
+### Resource Routes
+
+This section assumes you've read [working with controllers](/docs/routing/controller). In an MVC application, controllers play a major role as they're the bridge between your view and your model.
+
+A resource route simply creates all the routes needed to successfully handle a particular feature. This sounds a bit bleak, let's look at an example.
+
+```php
+Router::resource("/posts", "PostsController");
+
+Router::run();
+```
+
+The code above is equivalent to this:
+
+```php
+Router::match("GET|HEAD", "/posts", "$controller@index");
+Router::post("/posts", "$controller@store");
+Router::match("GET|HEAD", "/posts/create", "$controller@create");
+Router::match("POST|DELETE", "/posts/{id}/delete", "$controller@destroy");
+Router::match("POST|PUT|PATCH", "/posts/{id}/edit", "$controller@update");
+Router::match("GET|HEAD", "/posts/{id}/edit", "$controller@edit");
+Router::match("GET|HEAD", "/posts/{id}", "$controller@show");
+
+Router::run();
+```
+
+Resource routes are handled by a [resource controller](/docs/routing/controller?id=resource-controller).
+
+### Route "Hooking"
+
+You can add a route that handles a couple of HTTP methods with the Leaf router's match() method. It accepts three arguments:
+
+- The HTTP method(s) seperated by |
+- The route pattern (with optional named placeholders or PCRE based patterns)
+- The route callback
+
+```php
+Router::match('GET|POST', '/people', function () {
+ // your code
+});
+```
+
+### Running your routes
+
+After setting all the routes, you'll need to dispatch the routes. This is achieved through Leaf's run() method.
+
+```php
+Router::run();
+```
+
+### Route options
+
+This is the biggest change Leaf router has seen over the period of a year. Route options simply allow you to configure the way groups and individual routes by passing in additional parameters. In actual sense, all new features were generated as a result of this single feature. Let's see how it works.
+
+Leaf route handlers are usually callable functions like this:
+
+```php
+Router::get("/home", function () {
+ echo "User Home";
+});
+```
+
+Or sometimes controllers, like this:
+
+```php
+Router::get("/home", "HomeController@index");
+```
+
+This means there was no space to chain additional items to the route, this is solved by route options.
+
+```php
+Router::get("/home", ["name" => "home", function () {
+ echo "User Home";
+}]);
+```
+
+When an array is passed into a leaf route as the handler, leaf will take all `key => value` as options for that route, the first non key-value `function` or `controller` in the array is taken as the handler.
+
+```php
+Router::get("/form", ["name" => "userForm", "FormsController@index"]);
+```
+
+As mentioned before, this feature is also available on groups:
+
+```php
+Router::group("/user", ["namespace" => "\\", function () {
+ // ...
+}]);
+```
+
+**This doesn't mean that you should always pass in an array, if you don't need the other options, you can pass in your function or controller directly as you've always done.**
+
+### Naming your routes
+
+From v2.5.0 of Leaf, you can give route names which you can call them with instead of using the path (Inspired by vue-router).
+
+```php
+Router::get("/home", ["name" => "home", function () {
+ echo "User Home";
+}]);
+```
+
+### Pushing to a route
+
+This is simply redirecting to a route and can be done using `push`. `push` also allows you to reference the route by it's name instead of it's path.
+
+```php
+Router::push("/home");
+```
+
+When an array is passed into push, Leaf will search for a route name matching the string in the array and redirect to that route:
+
+```php
+// home was defined above
+Router::push(["home"]);
+```
diff --git a/apps/docs/src/modules/router/middleware.md b/apps/docs/src/modules/router/middleware.md
new file mode 100755
index 0000000..554ee5a
--- /dev/null
+++ b/apps/docs/src/modules/router/middleware.md
@@ -0,0 +1,137 @@
+# Middleware
+
+
+Middleware are just methods that run before your code runs, be it a particular route or your whole application. Unlike many other frameworks and systems, Leaf gives you the opportunity to set global middleware that run before any and every route.
+
+## ⏳ Before Route Middlewares
+
+Leaf's Core router supports Before Route Middlewares, which are executed before the route handling is processed.
+
+Like route handling functions, you hook a handling function to a combination of one or more HTTP request methods and a specific route pattern.
+
+```php
+$app->before('GET|POST', '/admin/.*', function () {
+ if (!isset($_SESSION['user'])) {
+ header('location: /auth/login');
+ exit();
+ }
+});
+```
+
+## Middleware route option
+
+This is a new way to quickly setup middleware for a particular route. Leaf has the before method which allows you to set a route specific middleware, but that means defining the same route twice, not to mention, you may mistake the middleware for the main route as they have the same syntax. This problem is solved by the middleware option. **If your prefer using `before`, you can always do so.**
+
+```php
+// you can define it in a different file
+$homeMiddleware = function () {
+ echo "Home middleware";
+};
+
+$app->get("/home", ["middleware" => $homeMiddleware, function () {
+ echo "User Home";
+}]);
+```
+
+Unlike route handling functions, more than one before route middleware is executed when more than one route match is found.
+
+## ✨ Before Router Middlewares
+
+Before route middlewares are route specific. Using a general route pattern (viz. all URLs), they can become Before Router Middlewares (in other projects sometimes referred to as before app middlewares) which are always executed, no matter what the requested URL is.
+
+```php
+$app->before('GET', '/.*', function () {
+ // ... this will always be executed
+});
+```
+
+### Router Hooks
+
+Hooks basically allow you to hook into Leaf router and execute a callback at a given time. For instance, you can execute a function just before Leaf fires off routes. You can also execute a callback before the main middleware executes or even after Leaf has completely executed a route.
+
+There are 6 hooks that you can now use with Leaf router listed below in execution order:
+
+#### `router.before`
+
+This hook runs before Leaf router begins any operations, even before app middleware are triggered.
+
+#### `router.before.route`
+
+This hook runs just after the app middleware have run, just before the route specific middleware.
+
+#### `router.before.dispatch`
+
+This hook runs just before routes are dispatched.
+
+#### `router.after.dispatch`
+
+This hook runs just after routes are dispatched.
+
+#### `router.after.route`
+
+This hook runs after Leaf router has finished up with routing and cleaning up, just before the execution of internal code.
+
+#### `router.after`
+
+This hook runs when leaf completely finishes route execution and cleans up on the internal code as well. This is the last thing Leaf router does before exiting.
+
+**Note** Unlike the above hooks, `router.after` can be directly assigned by passing a function into Leaf router's `run` method.
+
+```php
+$app = new Leaf\App;
+
+$app->run(function () {
+ echo "Final thing to run";
+});
+```
+
+Also note that the final function may return a value for further use if need be.
+
+```php
+$time = Leaf\Router::run(function () {
+ return Leaf\Date::now();
+});
+
+saveToLogs("app finished executing", $time);
+```
+
+To get started with hooks, simply use the `hook` method to define the hook you want to use.
+
+**It doesn't matter the order in which you define hooks. Leaf router will run them in the correct order.**
+
+```php
+$app->hook("router.before", function () {
+ Leaf\Http\Headers::resetStatus(202);
+});
+```
+
+The example above makes sure that every response gets sent with a `202 Accepted` https status instead of the original status code. As you can see, `hook` takes in the hook you wish to set and a callable/callback to execute.
+
+### App middleware
+
+::: warning
+If you are using leaf router outside of leaf, we suggest you use router hooks or before router middleware above.
+:::
+
+App middleware which are created using `Leaf\Middleware` have also received a lot of fixes which make them easier and faster to use.
+
+::: danger NOTE
+Router `add` has been renamed to `use`. This means that you will be using `use` to load your middleware instead.
+:::
+
+```php{14}
+// usually in a different file
+class Test extends Leaf\Middleware
+{
+ public function call()
+ {
+ echo "my test middleware";
+
+ $this->next();
+ }
+}
+
+$app = new Leaf\App;
+
+$app->use(new Test);
+```
diff --git a/apps/docs/src/modules/router/sub-folder.md b/apps/docs/src/modules/router/sub-folder.md
new file mode 100755
index 0000000..7f12c18
--- /dev/null
+++ b/apps/docs/src/modules/router/sub-folder.md
@@ -0,0 +1,37 @@
+---
+title: "Sub-folder support"
+---
+
+# Sub-folder support
+
+
+## 📖 Overview
+
+Out-of-the box Leaf's Core router will run in any (sub)folder you place it into … no adjustments to your code are needed. You can freely move your entry script index.php around, and the router will automatically adapt itself to work relatively from the current folder's path by mounting all routes onto that basePath.
+
+Say you have a server hosting the domain www.example.org using public_html/ as its document root, with this little entry script index.php:
+
+```php
+$app->get('/', function () { echo 'Index'; });
+$app->get('/hello', function () { echo 'Hello!'; });
+```
+
+- If your were to place this file (along with its accompanying .htaccess file or the like) at the document root level (e.g. public_html/index.php), Leaf's Core router will mount all routes onto the domain root (e.g. /) and thus respond to [https://www.example.org/](https://www.example.org/) and [https://www.example.org/hello](https://www.example.org/hello).
+
+- If you were to move this file (along with its accompanying .htaccess file or the like) into a subfolder (e.g. public_html/demo/index.php), Leaf's Core router will mount all routes onto the current path (e.g. /demo) and thus repsond to [https://www.example.org/demo](https://www.example.org/demo) and [https://www.example.org/demo/hello](https://www.example.org/demo/hello). There's no need for `$app->mount(…)` in this case.
+
+## Disabling subfolder support
+
+In case you don't want Leaf's Core router to automatically adapt itself to the folder its being placed in, it's possible to manually override the basePath by calling `setBasePath()`. This is necessary in the (uncommon) situation where your entry script and your entry URLs are not tightly coupled (e.g. when the entry script is placed into a subfolder that does not need be part of the URLs it responds to)..
+
+```php
+// Override auto base path detection
+$app->setBasePath('/');
+
+$app->get('/', function () { echo 'Index'; });
+$app->get('/hello', function () { echo 'Hello!'; });
+
+$app->run();
+```
+
+If you were to place this file into a subfolder (e.g. public_html/some/sub/folder/index.php), it will still mount the routes onto the domain root (e.g. /) and thus respond to [https://www.example.org/](https://www.example.org/) and [https://www.example.org/hello](https://www.example.org/hello) (given that your .htaccess file – placed at the document root level – rewrites requests to it)
diff --git a/apps/docs/src/modules/router/sub-patterns.md b/apps/docs/src/modules/router/sub-patterns.md
new file mode 100755
index 0000000..d34add4
--- /dev/null
+++ b/apps/docs/src/modules/router/sub-patterns.md
@@ -0,0 +1,41 @@
+---
+title: "Optional Route Subpatterns"
+---
+
+
+
+*This guide assumes you have read [Simple Routing](/docs/routing/) and [dynamic routing](/docs/routing/dynamic)*
+
+Route subpatterns can be made optional by making the subpatterns optional by adding a ? after them. Think of blog URLs in the form of /blog(/year)(/month)(/day)(/slug):
+
+```php
+$app->get('/blog(/\d+)?(/\d+)?(/\d+)?(/[a-z0-9_-]+)?', function ($year = null, $month = null, $day = null, $slug = null) {
+ if (!$year) { echo 'Blog overview'; return; }
+ if (!$month) { echo 'Blog year overview'; return; }
+ if (!$day) { echo 'Blog month overview'; return; }
+ if (!$slug) { echo 'Blog day overview'; return; }
+ echo 'Blogpost ' . htmlentities($slug) . ' detail';
+});
+```
+
+The code snippet above responds to the URLs /blog, /blog/year, /blog/year/month, /blog/year/month/day, and /blog/year/month/day/slug.
+
+**Note**: With optional parameters it is important that the leading / of the subpatterns is put inside the subpattern itself. Don't forget to set default values for the optional parameters.
+
+The code snipped above unfortunately also responds to URLs like /blog/foo and states that the overview needs to be shown - which is incorrect. Optional subpatterns can be made successive by extending the parenthesized subpatterns so that they contain the other optional subpatterns: The pattern should resemble /blog(/year(/month(/day(/slug)))) instead of the previous /blog(/year)(/month)(/day)(/slug):
+
+```php
+$app->get('/blog(/\d+(/\d+(/\d+(/[a-z0-9_-]+)?)?)?)?', function ($year = null, $month = null, $day = null, $slug = null) {
+ // ...
+});
+```
+
+**Note**: It is highly recommended to always define successive optional parameters.
+
+To make things complete use [quantifiers](http://www.php.net/manual/en/regexp.reference.repetition.php) to require the correct amount of numbers in the URL:
+
+```php
+$app->get('/blog(/\d{4}(/\d{2}(/\d{2}(/[a-z0-9_-]+)?)?)?)?', function ($year = null, $month = null, $day = null, $slug = null) {
+ // ...
+});
+```
diff --git a/apps/docs/src/modules/router/sub-routing.md b/apps/docs/src/modules/router/sub-routing.md
new file mode 100755
index 0000000..7d752e8
--- /dev/null
+++ b/apps/docs/src/modules/router/sub-routing.md
@@ -0,0 +1,58 @@
+# Sub-routing
+
+
+Use `app()->mount($baseroute, $fn)` or `app()->group` to mount a collection of routes onto a subroute pattern. The subroute pattern is prefixed onto all following routes defined in the scope. e.g. Mounting a callback $fn onto `/movies` will prefix `/movies` onto all following routes.
+
+```php
+app()->mount('/movies', function () {
+ // will result in '/movies/'
+ app()->get('/', function () {
+ echo 'movies overview';
+ });
+
+ // will result in '/movies/id'
+ app()->get('/(\d+)', function ($id) {
+ echo 'movie id ' . htmlentities($id);
+ });
+});
+```
+
+Nesting of subroutes is possible, just define a second `app()->mount()` in the callback function that's already contained within a preceding `app()->mount()`. Also, Note that nested subroutes currently don't support dynamic url patterns, so, you can only do something like this.
+
+```php
+app()->group('/user', function () {
+ app()->get('/', function () {
+ response()->markup('no user id');
+ });
+
+ app()->get('/(\d+)', function ($id) {
+ response()->markup("user $id");
+ });
+
+ app()->mount('/settings', function () {
+ app()->get('/privacy', function () {
+ response()->markup('Privacy Settings');
+ });
+
+ app()->get('/notification', function () {
+ response()->markup("Notification Settings");
+ });
+ });
+});
+```
+
+## Group Namespaces
+
+You can now select namespaces for individual groups of routes. Usually, a namespace is given to all your routes, however, a group may need a different namespace for it's controllers and that is what Leaf gives you.
+
+```php
+app()->setNamespace("App\Controllers");
+
+app()->group("/user", ["namespace" => "Lib\Controllers", function () {
+ // controller here will be Lib\Controllers\FormsController
+ app()->get("/form", "FormsController@index");
+}]);
+
+// controller here will be App\Controllers\FormsController
+app()->get("/form", "FormsController@index");
+```
diff --git a/apps/docs/src/modules/session/flash.md b/apps/docs/src/modules/session/flash.md
new file mode 100755
index 0000000..20b54e1
--- /dev/null
+++ b/apps/docs/src/modules/session/flash.md
@@ -0,0 +1,211 @@
+
+# Leaf Flash
+
+Session flash messages are temporary messages that are stored in the session and displayed to the user for a short duration, usually just until they are shown once. These messages are commonly used to provide feedback or notifications to the user after a specific action or event, such as successfully completing a form submission, encountering an error, or performing a specific operation.
+
+Leaf session provides a simple interface for creating and managing flash messages. Flash messages are stored in the session and can be retrieved and displayed in the view.
+
+## Config
+
+Leaf Flash comes with a default configuration that works out of the box. However, you can change the configuration to suit your needs. You can skip this section if you're using the default configuration.
+
+Using the `config()` method, you can change where Leaf stores flash messages in session, the keys for messages and saved content. The available options are:
+
+- key: The key to save flash array in session. Default: `leaf.flash`,
+- default: The key for default flash messages. Default: `message`,
+- saved: The key for saved flash messages. Default: `leaf.flashSaved`,
+
+
+
+```php
+flash()->config([
+ 'key' => 'my_flash_items'
+]);
+
+flash()->set('This is my message');
+
+// logging $_SESSION
+=> ['my_flash_items' => ['message' => 'This is my message']]
+```
+
+
+
+
+```php
+use Leaf\Flash;
+
+Flash::config([
+ 'key' => 'my_flash_items'
+]);
+
+Flash::set('This is my message');
+
+// logging $_SESSION
+=> ['my_flash_items' => ['message' => 'This is my message']]
+```
+
+
+
+**Flash searches for an existing session and creates one if there's no active session.**
+
+## Adding a new flash
+
+Leaf Flash provides a `set()` method for adding new flash messages to the session. The `set()` method accepts two arguments:
+
+- The item to flash
+- The key to save it under. The key is optional and defaults to `message`.
+
+
+
+```php
+flash()->set('This is my message');
+flash()->set('This is my message', 'info');
+
+// accepts different types of data
+flash()->set($userObject);
+flash()->set($userArray);
+flash()->set($userString);
+flash()->set($userInt);
+```
+
+
+
+
+```php
+Leaf\Flash::set('This is my message');
+Leaf\Flash::set('This is my message', 'info');
+
+// accepts different types of data
+Leaf\Flash::set($userObject);
+Leaf\Flash::set($userArray);
+Leaf\Flash::set($userString);
+Leaf\Flash::set($userInt);
+```
+
+
+
+Leaf flash allows you to keep multiple flash messages at the same time. They won't be removed until you show them. To do this, you will need to set different keys for each flash message.
+
+
+
+```php
+flash()->set('This is my message', 'info');
+flash()->set('This is my message', 'error');
+flash()->set('This is my message', 'success');
+```
+
+
+
+
+```php
+Leaf\Flash::set('This is my message', 'info');
+Leaf\Flash::set('This is my message', 'error');
+Leaf\Flash::set('This is my message', 'success');
+```
+
+
+
+## Displaying a flash message
+
+Leaf Flash provides a `display()` method that you can use to retrieve your session flash. As soon as the flash message is retrieved, it is removed from the session which means it won't show on the next load.
+
+
+
+## Remove a flash message
+
+The `unset()` method allows you to remove a flash message. Note that flash messages are automatically removed when you display them, so you might never need to use this method.
+
+
+
+```php
+flash()->unset();
+flash()->unset('info'); // unset a specific flash message
+```
+
+
+
+
+```php
+Leaf\Flash::unset();
+Leaf\Flash::unset('info'); // unset a specific flash message
+```
+
+
+
+## Permanent flash messages
+
+Flash also allows you to create a message that stays in session till it is manually removed. You can add permanently stored flash data using the `save()` method. Note that unlike regular flashes, there can be only one saved flash message.
+
+
+
+```php
+flash()->save('This is my message');
+```
+
+
+
+
+```php
+Leaf\Flash::save('This is my message');
+```
+
+
+
+## Removing saved flash messages
+
+You can use the `clearSaved()` method to remove saved flash messages.
+
+
+
+```php
+flash()->clearSaved();
+```
+
+
+
+
+```php
+Leaf\Flash::clearSaved();
+```
+
+
+
+## Displaying saved flash messages
+
+You can use the `displaySaved()` method to display saved flash messages.
+
+
diff --git a/apps/docs/src/modules/session/index.md b/apps/docs/src/modules/session/index.md
new file mode 100755
index 0000000..75dd700
--- /dev/null
+++ b/apps/docs/src/modules/session/index.md
@@ -0,0 +1,649 @@
+
+# Leaf Session
+
+Given that HTTP-driven applications lack statefulness, sessions offer a means of retaining user-related data throughout multiple requests. Usually, this user information is stored in some sort of persistent storage so it can be accessed on subsequent requests.
+
+Although using sessions in PHP is fairly straightforward, it can be a bit cumbersome at times. Leaf session aims to make working with sessions in PHP much easier, more enjoyable, and more testable. For that reason, Leaf provides the session module to help you manage sessions in your Leaf apps. *Leaf session is 100% compatible with native PHP sessions.*
+
+## Installation
+
+You can install leaf session with the Leaf CLI tool:
+
+```bash
+leaf install session
+```
+
+or with composer:
+
+```bash
+composer require leafs/session
+```
+
+## Functional Mode
+
+When using Leaf session in a Leaf 3 app, you can use the functional mode. This allows you to use the session module without having to initialize it. This gives you access to the `session()` and [`flash()`](/modules/session/flash#functional-mode) helper functions.
+
+## Starting a new session
+
+Leaf's session module smartly handles session initialization. It checks if a session has already been started and starts a new one if it hasn't. This means you don't have to worry about starting a session before using it. Note that Leaf will not start a session until you actually use it. This is to prevent unnecessary session initialization.
+
+You also don't have to worry about messing up your sessions since Leaf session is 100% compatible with native PHP sessions.
+
+### Manually starting a session
+
+If you want to manually start a session, you can use the `start()` method.
+
+
+
+## Retrieving session data
+
+Leaf session provides 3 ways to retrieve session data:
+
+- The `get()` method
+- The `retrieve()` method
+- The `body()` method
+
+### The `get()` method
+
+`get()` is a simple method that returns a session value. It takes in 3 parameters:
+
+- The name of the value to return. It works just like how `$_SESSION['key']` does.
+- The default value to use if it doesn't exist.
+- A boolean value indicating whether to sanitize the value or not. It has a default value of true.
+
+
+
+### The `retrieve()` method
+
+`retrieve()` returns the requested value just like `get()` above **and then immediately removes it from the session**, so it can only be retrieved once.
+
+It takes in three parameters:
+
+- The name of the value to return. It works just like how `$_SESSION['key']` does.
+- The default value to use if it doesn't exist.
+- A boolean value indicating whether to sanitize the value or not. It has a default value of true.
+
+
+
+### The `body()` method
+
+This method returns the {key => value} pairs of all the session data including any CSRF data as an associative array.
+
+
+
+## Check if a session value exists
+
+You can check if a session value exists with the `has()` method. It takes in the name of the value to check for and returns a boolean value.
+
+
+
+```php
+$session = new Leaf\Http\Session;
+
+...
+
+if ($session->has('item')) {
+ // do something
+}
+```
+
+
+
+By default, `has()` will return `true` **ONLY if the value exists and is NOT empty**. You can change this behaviour by passing in `false` as the second parameter. This boolean value indicates whether to check if the value is empty or not. It has a default value of true.
+
+
+
+## Setting session data
+
+The session module provides a simple way to set session data using the `set()` method. It takes in two parameters:
+
+- The name of the value to set.
+- The value to set.
+
+
+
+## Wiping session data
+
+Leaf Session allows you to wipe all session data with the `clear()` method. This method completely deletes all session information, but does not destroy the session itself.
+
+
+
+## Working with Arrays
+
+Leaf session allows you to neatly handle working with arrays in session. You can set, retrieve and validate session values from arrays by using the dot notation:
+
+
+
+## Session flash
+
+Leaf session also provides built-in support for flash messages. Flash messages are temporary messages that are displayed to the user after an action has been performed. They are usually used to display success or error messages to the user.
+
+
+
+```php
+$session = new Leaf\Http\Session;
+
+$session->flash("my flash message");
+
+echo $session->flash(); // my flash message
+```
+
+
+
+For more advanced uses of flash messages, you can check out the [Flash Session docs](/modules/session/flash).
+
+## Session Internals
+
+Leaf session provides a few methods to help you manage sessions. These methods are:
+
+### reset()
+
+You can use the `reset()` method to re-initialize a session.
+
+
+
+### id()
+
+`id()` sets and/or returns the current session id. It takes in an **optional** parameter: the ID to overwrite the session id.
+
+
+
+```php
+$id = $session->id();
+```
+
+
+
+
+```php
+$id = session()->id();
+```
+
+
+
+If the session id is not set, this will generate and return a new session id. However, if the session id is already set, it will just return it.
+
+You can also set your own session id with this syntax below. It will be returned as well, so you can keep it in a variable.
+
+
+
+### regenerate()
+
+This method generates a new session id. It takes in a boolean parameter which indicates whether to delete all session data or not (has a default of false)
+
+
+
+```php
+$session->regenerate();
+$session->regenerate(false);
+$session->regenerate(true); // will clear all session data
+```
+
+
+
+
+```php
+session()->regenerate();
+session()->regenerate(false);
+session()->regenerate(true); // will clear all session data
+```
+
+
+
+### destroy()
+
+You can end a session with `destroy`.
+
+
+
+```php
+$session->destroy();
+```
+
+
+
+
+```php
+session()->destroy();
+```
+
+
+
+### encode()
+
+This feature allows you to encode the current session data as a string.
+
+
+
+### decode()
+
+You can also decode a serialized session using the `decode()` method. It takes in the string to decode and returns true on success, false on failure.
+
+
diff --git a/apps/docs/src/modules/views/bareui/index.md b/apps/docs/src/modules/views/bareui/index.md
new file mode 100644
index 0000000..7b8d0c5
--- /dev/null
+++ b/apps/docs/src/modules/views/bareui/index.md
@@ -0,0 +1,281 @@
+# BareUI
+
+
+
+
+
+Most templating engines out there ship with a nice syntax, handy ways to use expressions, layouts and code blocks, however, there's one problem: speed! Bare UI is here to solve that. Bare UI is a barebones templating engine focused on speed, speed and more speed! It lacks all the syntactic sugar added in other engines like blade, but it also requires no compiling, no caching, just speed!
+
+
+New to template engines?
+
+Watch this video by Dave Hollingworth as an introduction to template engines.
+
+
+
+
+## Installation
+
+You can always install BareUI with the Leaf CLI:
+
+```bash
+leaf install bareui
+```
+
+Or with composer:
+
+```bash
+composer require leafs/bareui
+```
+
+## Usage with Leaf MVC
+
+Leaf MVC and Leaf API come with [Leaf Blade](/modules/views/blade/) out of the box, however, since Leaf is modular at it's core, Leaf MVC and Leaf API allow you easily swap out the blade engine for BareUI (or any other view engine). To do this, you need to swap out the blade engine in your `public/index.php`:
+
+```php
+// public/index.php
+Leaf\View::attach(\Leaf\Blade::class); // remove this
+Leaf\View::attach(\Leaf\BareUI::class); // add this
+```
+
+After this, you will also need to change the config in `config/view.php`:
+
+```php
+ \Leaf\BareUI::class,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom config method
+ |--------------------------------------------------------------------------
+ |
+ | Configuration for your templating engine.
+ |
+ */
+ 'config' => function ($config) {
+ View::bareui()->config($config['views']);
+ },
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom render method
+ |--------------------------------------------------------------------------
+ |
+ | This render method is triggered whenever render() is called
+ | in your app if you're using a custom view engine.
+ |
+ */
+ 'render' => function ($view, $data = []) {
+ return View::bareui()->render($view, $data);
+ },
+];
+```
+
+This will tell Leaf how to work with BareUI.
+
+## Introduction
+
+BareUI takes a fully static approach which means you can use all of it's methods without having to initilaize the package first. Also, there are only 2 things you need to keep in mind about Bare UI
+
+::: tip Quick Tip
+BareUI has deep integrations with Leaf core by default. This means that if you're using BareUI in leaf, it will always be available on the leaf instance as `template`.
+
+
+
+:::
+
+### config
+
+This method allows you to configure bare ui.
+
+```php
+Leaf\BareUI::config('path', './templates');
+
+// or
+
+Leaf\BareUI::config(['path' => './templates', ...]);
+```
+
+#### Available options
+
+There are currently only 2 options to configure.
+
+- path (string): This tells leaf where to look for templates.
+- params (array): These are a bunch of base parameters that will be available in all of your templates.
+
+
+
+```php
+// app() will be available in all templates
+app()->template->config('params', ['app' => function () {
+ // do something
+ return app();
+}]);
+```
+
+
+
+
+```php
+// app() will be available in all templates
+$app->template->config('params', ['app' => function () use ($app) {
+ // do something
+ return $app;
+}]);
+```
+
+
+
+`template.view.php`
+
+
+
+```php
+template->render('error');
+}
+```
+
+
+
+
+```php
+template->render('error');
+}
+```
+
+
+
+### render
+
+As the name implies, this method allows you to return an bare ui. It takes in the UI to render.
+
+::: tip NOTE
+BareUI files end with `.view.php`. This allows you easily distinguish them from the rest of your files.
+:::
+
+Let's look at a simple template:
+
+`welcome.view.php`
+
+```php
+
+
+
+
+
+
+ Document
+
+
+
+
+
+```
+
+We can render this from where we want the UI to show:
+
+
+
+
+
+```
diff --git a/apps/docs/src/modules/views/blade/index.md b/apps/docs/src/modules/views/blade/index.md
new file mode 100755
index 0000000..1bbd30f
--- /dev/null
+++ b/apps/docs/src/modules/views/blade/index.md
@@ -0,0 +1,116 @@
+# Leaf Blade
+
+
+
+
+
+[Blade](https://laravel.com/docs/10.x/blade#introduction) is the simple, yet powerful templating engine that is included with Laravel. Unlike some PHP templating engines, Blade does not restrict you from using plain PHP code in your templates. In fact, all Blade templates are compiled into plain PHP code and cached until they are modified, meaning Blade adds essentially zero overhead to your application. Blade template files use the `.blade.php` file extension.
+
+Leaf Blade is a port of the [jenssegers/blade](https://github.com/jenssegers/blade) package that allows you to use blade templates in your Leaf PHP projects.
+
+
+
+## Usage with Leaf MVC
+
+Leaf MVC and Leaf API come with blade support out of the box. You can use blade templates in your Leaf MVC and Leaf API projects without any extra configuration.
+
+All you need to do is create a new blade file in your `app/views` folder and you're good to go! You can also create sub-folders in your `app/views` folder to organize your blade files into multiple sections.
+
+You can skip the installation and setup sections if you're using Leaf MVC or Leaf API.
+
+## Usage with Leaf Core
+
+Since Leaf's core doesn't give you any structure, you'll have to set up blade yourself. Don't worry, it's pretty easy. All you need to do is install blade, configure it to match your project's setup and you're good to go.
+
+You can install leaf blade with the Leaf CLI:
+
+```bash
+leaf install blade
+```
+
+Or with composer:
+
+```bash
+composer require leafs/blade
+```
+
+After this, you simply need to initialize blade:
+
+```php
+$blade = new Leaf\Blade();
+```
+
+## The Blade class
+
+The Blade class takes two parameters on initialization. The first is the path to your view files, and the second is the path to your cached files.
+
+```php
+use Leaf\Blade;
+
+$blade = new Blade('views', 'storage/cache');
+```
+
+If you want to initialize the package with the default settings, you can simply do:
+
+```php
+$blade = new Leaf\Blade();
+```
+
+And then configure it later using the `configure()` method:
+
+```php
+$blade->configure('app/views', 'app/views/cache');
+```
+
+## Rendering a blade view
+
+Once you have pointed Blade to the location of your view files, you may easily render them using the `make` method. The `make` method accepts the name of the view file as its first argument, and an array of data as its second argument. The data array will be extracted into variables that may be used within the view file:
+
+```php
+echo $blade->make('index', ['name' => 'Michael Darko'])->render();
+```
+
+Alternatively you can use the shorthand `render()` method:
+
+```php
+echo $blade->render('index', ['name' => 'Michael Darko']);
+```
+
+The examples above will look for an `index.blade.php` file in the `views` directory. Within the view, you can access the `name` variable like so:
+
+```html
+
+
+
+ {{ $name }}
+
+
+
{{ $name }}
+
+
+```
+
+## Extending Blade
+
+Blade allows you to define custom directives using the `directive()` method. When the Blade compiler encounters the custom directive, it will call the provided callback with the expression that the directive contains. The callback is free to return the value of its contents however you like:
+
+```php
+$blade->directive('datetime', function ($expression) {
+ return "format('F d, Y g:i a'); ?>";
+});
+```
+
+Which allows you to use the following in your blade template:
+
+```html
+Current date: @datetime($date)
+```
+
+The Blade instance passes all methods to the internal view factory. So methods such as exists, file, share, composer and creator are available as well. Check out the [original documentation](http://laravel.com/docs/5.8/blade) for more information.
diff --git a/apps/docs/src/modules/views/index.md b/apps/docs/src/modules/views/index.md
new file mode 100644
index 0000000..e9cbfde
--- /dev/null
+++ b/apps/docs/src/modules/views/index.md
@@ -0,0 +1,46 @@
+# Frontend
+
+As a backend inclined framework, leaf has always focused on tooling for building APIs and backend applications. However, as frontend frameworks and libraries have become more popular, leaf has also evolved to support some modern frontend tooling which can be used to build amazing full-stack applications.
+
+This section of the docs will cover all the frontend tooling leaf has to offer.
+
+## Templating Engines
+
+Over the years, leaf has had suport for many built-in templating engines. All of these templating engines have first class support in leaf and can be used to build amazing frontend applications. These engines are available as separate packages and can be installed using composer, which means you can use them outside leaf apps as well.
+
+Depending on your needs, you may want to go with a particular templating engine.
+
+| Engine | Use case |
+| -------------------------------- | -------------------------------------------- |
+| [bareui](/modules/views/bareui/) | Blazing fast templating with no compile time |
+| [veins](/modules/views/veins/) | Lightweight but powerful templating engine |
+| [blade](/modules/views/blade/) | Laravel blade templating engine for leaf |
+
+### BareUI vs Veins vs Blade
+
+| Engine | Speed | Cool Magic | Lightweight | Editor Support |
+| -------------------------------- | :-----: | :----------: | :-----------: | :------------: |
+| [bareui](/modules/views/bareui/) | ⚡️ | ❌ | ⚡️ | ⚡️ |
+| [veins](/modules/views/veins/) | 🔥 | 🔥 | 🔥 | 🔥 |
+| [blade](/modules/views/blade/) | ❌ | ⚡️ | 🔥 | ⚡️ |
+
+### Third Party Templating Engines
+
+Although Leaf has some preferred templating engines, you can use any templating engine you want with leaf. Here are some of the most popular templating engines you can use with leaf:
+
+- [Twig](https://twig.symfony.com/)
+- [Smarty](https://www.smarty.net/)
+
+You can check out the [third party templating engines](/modules/views/third-party/) section of the docs to learn how to use any templating engine with leaf.
+
+## Asset Bundling
+
+[Vite](https://vitejs.dev/) is a modern build tool for frontend applications. It aims to provide a faster and leaner development experience for modern web projects. Leaf allows you to bundle your CSS and JS assets using vite, using the powerful [leaf-vite](/modules/views/vite/) module.
+
+[> Read the docs](/modules/views/vite/)
+
+## Frontend Frameworks
+
+Leaf has support for some of the most popular frontend frameworks using [Inertia.js](https://inertiajs.com/). Inertia.js is a framework that allows you to create fully client-side rendered, single-page apps, without much of the complexity that comes with modern SPAs. It does this by leveraging Leaf's server-side rendering capabilities.
+
+[> Read the docs](/modules/views/inertia/)
diff --git a/apps/docs/src/modules/views/inertia/index.md b/apps/docs/src/modules/views/inertia/index.md
new file mode 100644
index 0000000..02d7aa3
--- /dev/null
+++ b/apps/docs/src/modules/views/inertia/index.md
@@ -0,0 +1,332 @@
+# Inertia JS
+
+[Inertia](https://inertiajs.com/) is a new approach to building classic server-driven web apps. It allows you to create fully client-side rendered, single-page apps, without the complexity that comes with modern SPAs. It does this by leveraging existing server-side patterns that you already love.
+
+## Usage with Leaf MVC
+
+Leaf MVC and Leaf API come with built-in support for Inertia.js. This means you can build amazing frontend applications using Inertia.js and Leaf right out of the box without any extra configuration.
+
+Leaf CLI comes with a `view:install` command to help you get on your way as fast as possible. Since inertia provides support for a variety of frontend frameworks, this command will help you install and setup inertia for your preferred frontend framework.
+
+To get started, run:
+
+```bash
+php leaf view:install
+```
+
+Or with the Leaf CLI:
+
+```bash
+leaf view:install
+```
+
+This will prompt you to select your preferred frontend framework. You can choose from Vue, React, and Svelte. There is also support for styling with Tailwind/Bootstrap. After selecting your preferred framework, Leaf will automatically install and setup inertia for you, including examples for you to get started with.
+
+::: tip view:install
+If you know the specific frontend framework you want to use, you can pass the `--{framework}` flag to the `view:install` command. For example, to install inertia for Vue, you can run:
+
+```bash
+php leaf view:install --vue
+```
+
+:::
+
+To run your app, you should start the server for both your frontend and backend. To start the backend server, run:
+
+```bash
+php leaf serve
+
+# or with the Leaf CLI
+
+leaf serve
+```
+
+To start the frontend server, run:
+
+```bash
+npm run dev
+
+# or with the Leaf CLI
+
+php leaf view:dev
+leaf view:dev
+```
+
+## Usage with Leaf Core
+
+Inertia has been fine-tuned to work with Leaf MVC and Leaf API. However, you can still use inertia with Leaf Core, however, you'll need to install and setup inertia yourself. There are 2 ways to do this:
+
+- [Using the Leaf CLI](#using-the-leaf-cli)
+- [Manually setting up inertia](#manually-setting-up-inertia)
+
+::: tip This guide
+This guide also applies to using Inertia outside of Leaf applications. You can use this guide to setup inertia for any PHP application.
+:::
+
+### Using the Leaf CLI
+
+The Leaf CLI comes with a `view:install` command to help you get on your way as fast as possible. Since inertia provides support for a variety of frontend frameworks, this command will help you install and setup inertia for your preferred frontend framework.
+
+To get started, run:
+
+```bash
+leaf view:install
+```
+
+This will prompt you to select your preferred frontend framework. You can choose from Vue, React, and Svelte. There is also support for styling with Tailwind/Bootstrap. After selecting your preferred framework, Leaf will automatically install and setup inertia for you, including examples for you to get started with.
+
+::: tip view:install
+If you know the specific frontend framework you want to use, you can pass the `--{framework}` flag to the `view:install` command. For example, to install inertia for Vue, you can run:
+
+```bash
+leaf view:install --vue
+```
+
+:::
+
+Using this will install and setup Vite and inertia for you, however, depending on your own setup, you might need to make some changes to the setup. For example, you might need to change the entry-point for Vite. You can do this by changing the `input` option in your vite config file.
+
+```js
+leaf({
+ input: ['js/app.jsx'],
+ ...
+}),
+```
+
+Read more about setting up Vite [here](/modules/views/vite/#vite-config).
+
+To run your app, you should start the server for both your frontend and backend. To start the backend server, run:
+
+```bash
+leaf serve
+```
+
+To start the frontend server, run:
+
+```bash
+npm run dev
+```
+
+### Manually setting up inertia
+
+If you don't want to use the Leaf CLI, you can manually setup inertia. This guide will show you how to setup inertia with Vite and React. You can use this guide to setup inertia with any frontend framework.
+
+### Setting up Vite
+
+To get started, you need to setup Vite. We have a Leaf plugin that takes care of a lot of the heavy lifting for you. We have a detailed guide on how to setup vite with Leaf [here](/modules/views/vite/#usage-with-leaf-core).
+
+```bash
+npm i -D vite @leafphp/vite-plugin
+leaf install vite
+```
+
+### Vite Config
+
+The [Leaf Vite docs](/modules/views/vite/#vite-config) have a detailed guide on how to setup vite config files. You should however note that for the best developer experience, you should point Vite to your view directory so you can enjoy hot module reloading.
+
+```js
+...
+
+export default defineConfig({
+ plugins: [
+ leaf({
+ ...
+ refresh: ['yourviews/**'],
+ }),
+ ],
+});
+```
+
+Also note that your entry-point should be your base JavaScript file. For the best experience, CSS and other assets should be imported from your base JavaScript file.
+
+```js
+leaf({
+ input: ['js/app.jsx'],
+ ...
+}),
+```
+
+### Setting up Inertia
+
+To setup inertia, you need to install the inertia package for whatever frontend framework you want to use, together with the Vite plugin for that framework. For example, if you want to use React, you should install the Inertia React package, React Vite plugin as well as React itself:
+
+```bash
+npm i react react-dom @inertiajs/react @vitejs/plugin-react
+```
+
+You should also install the Leaf Inertia PHP adapter:
+
+```bash
+leaf install inertia
+```
+
+Or with composer:
+
+```bash
+composer require leafs/inertia
+```
+
+After adding the React Vite plugin, you should add it to your vite config file:
+
+```js{3,10}
+import { defineConfig } from 'vite';
+import leaf from '@leafphp/vite-plugin';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [
+ leaf({
+ ...
+ }),
+ react(),
+ ],
+});
+```
+
+### Setting up your base JavaScript file
+
+You should create a base JavaScript file that will be used to mount your app. This file should import your CSS and other assets. For example, if you're using React, your base JavaScript file should look like this:
+
+```jsx
+import { createRoot } from 'react-dom/client';
+import { createInertiaApp } from '@inertiajs/react';
+import { resolvePageComponent } from '@leafphp/vite-plugin/inertia-helpers';
+
+const appName = import.meta.env.VITE_APP_NAME || 'Leaf PHP';
+
+createInertiaApp({
+ title: (title) => `${title} - ${appName}`,
+ resolve: (name) =>
+ resolvePageComponent(
+ `./DIRECTORYFORCOMPONENTS/${name}.jsx`,
+ import.meta.glob('./DIRECTORYFORCOMPONENTS/**/*.jsx')
+ ),
+ setup({ el, App, props }) {
+ createRoot(el).render();
+ },
+});
+```
+
+`DIRECTORYFORCOMPONENTS` is the directory where your React pages are located. You can change this to whatever you want. You should also change the `setup` function to match your frontend framework. For example, if you're using Vue, you should change the `setup` function to:
+
+```js
+setup({ el, App, props }) {
+ createApp({
+ render: () => h(App, props),
+ }).mount(el);
+},
+```
+
+### Setting up your base PHP file
+
+You should create a base PHP file that will be used to render your app. By default, the Leaf Inertia PHP adapter will look for a file named `_inertia.view.php` in your views directory. You can change this by passing the path to your base PHP file to the `Inertia::setRoot` method.
+
+```php
+Inertia::setRoot('myfiles/_base');
+```
+
+Since the Leaf Inertia PHP adapter is built using the [Bare UI engine](/modules/views/bareui/), your base file needs to maintain the `.view.php` extension. For example, if you're using React, your base PHP file should look like this:
+
+```php
+
+
+
+
+
+
+ Document
+
+
+ dispatch($page);
+ }
+
+ if ($__inertiaSsrResponse) {
+ echo $__inertiaSsrResponse->head;
+ }
+ ?>
+
+
+
+ dispatch($page);
+ }
+
+ if ($__inertiaSsrResponse) {
+ echo $__inertiaSsrResponse->body;
+ } else {
+ echo '';
+ }
+ ?>
+
+
+
+```
+
+This might look pretty ugly, but you'll never have to touch this file again. You can also use the Leaf CLI to generate this file for you:
+
+```bash
+leaf view:install --inertia
+```
+
+### Setting up your frontend framework
+
+In the setup above, we told Inertia to look for our frontend framework files in `./DIRECTORYFORCOMPONENTS/`. You should create this directory and add your frontend framework files to it. For example, if you're using React, you should create a file named `Home.jsx` in this directory:
+
+```jsx
+const Home = () => {
+ return (
+
+
Hello World
+
+ );
+};
+
+export default Home;
+```
+
+## Setting up your routes
+
+Your routing does not change when using Inertia. You can use the same routing you use for your backend. For example, if you're using Leaf, define your routes the same way:
+
+```php
+app()->get('/', function () {
+ echo 'This is a route';
+});
+```
+
+The only difference is that you need to return an Inertia response instead of a normal response. You can do this by using the `Inertia::render` method:
+
+```php
+app()->get('/', function () {
+ Inertia::render('Home');
+});
+```
+
+The `Home` argument is the name of the file you created in `./DIRECTORYFORCOMPONENTS/`. You can also pass data to your frontend framework by passing an array as the second argument to the `Inertia::render` method:
+
+```php
+app()->get('/', function () {
+ return Inertia::render('Home', [
+ 'name' => 'Leaf',
+ ]);
+});
+```
+
+You can then access this data in your frontend framework as props. For example, if you're using React, you can access the `name` prop like this:
+
+```jsx
+const Home = ({ name }) => {
+ return (
+
+
Hello {name}
+
+ );
+};
+
+export default Home;
+```
diff --git a/apps/docs/src/modules/views/leaf-ui/index.md b/apps/docs/src/modules/views/leaf-ui/index.md
new file mode 100644
index 0000000..b87def5
--- /dev/null
+++ b/apps/docs/src/modules/views/leaf-ui/index.md
@@ -0,0 +1,122 @@
+
+
+# Leaf UI Beta
+
+Leaf UI is a PHP library that allows you to build modern web apps and scaffold dynamic and interactive UIs using the same PHP you already know and love. It's fast, easy, and fun. Leaf UI follows the component-based UI paradigm, which makes it simple to build reusable components that sync with the DOM automatically.
+
+
+
+This guide only covers how to use Leaf UI in your Leaf apps. For a more detailed explanation of how Leaf UI works, including explanations of its core principles and concepts, check out the [Leaf UI documentation](https://ui.leafphp.dev).
+
+## Installation
+
+You can quickly install Leaf UI with the Leaf CLI:
+
+```bash
+leaf install ui
+```
+
+Or with composer:
+
+```bash
+composer require leafs/ui
+```
+
+## Getting Started
+
+To get started, you need to create a new Leaf UI component. A component is a reusable piece of your website or app's UI, such as a header, sidebar, or button. A component includes HTML markup and the business logic needed to make that markup interactive. You can think of a component as a self-contained module that encapsulates its own functionality, which can then be reused throughout your website or app. You can also pass data into a component, making it more dynamic and reusable. Check out the [Leaf UI documentation](https://ui.leafphp.dev/docs/essentials/components.html) for more information on components.
+
+To create a new component, we need to add a new class. Let's create a new component called `HelloWorld`:
+
+```php
+
+
Hello World!
+
+
Hello, {$name}!
+
+ HTML;
+ }
+}
+```
+
+We can make this component more dynamic by passing data into our view. Let's add a `greeting` property and a `sayHello()` method to our component and use it in our markup:
+
+```php
+greeting = 'Hi World!';
+ }
+
+ public function render()
+ {
+ return <<
+
{{ $greeting }}
+
+
+ HTML;
+ }
+}
+```
+
+## Rendering Components
+
+Now that we have our component, we can use it in our Leaf app. Let's create a new route and return our component. We can do this by calling `render()` on the `UI` class and passing in our component:
+
+
diff --git a/apps/docs/src/modules/views/third-party/index.md b/apps/docs/src/modules/views/third-party/index.md
new file mode 100644
index 0000000..8e76250
--- /dev/null
+++ b/apps/docs/src/modules/views/third-party/index.md
@@ -0,0 +1,126 @@
+# Using third-party templates
+
+In the [previous section](/modules/views/), we talked about the template engines that Leaf supports as modules. However, you can use any template engine you want with the framework. In this section, we will show you how to use the [Smarty](https://www.smarty.net/) template engine with Leaf.
+
+## Install your engine
+
+To install Smarty, run the following command in your terminal:
+
+```bash
+composer require smarty/smarty
+```
+
+## Usage with Leaf Core
+
+Usage with Leaf's core is as straightforward as it gets. All you need to do is create a new instance of Smarty and use it as you would normally use it.
+
+```php
+setTemplateDir('/some/template/dir');
+$smarty->setConfigDir('/some/config/dir');
+$smarty->setCompileDir('/some/compile/dir');
+$smarty->setCacheDir('/some/cache/dir');
+
+// where you want to display the template
+$smarty->assign('name', 'Ned');
+$smarty->display('index.tpl');
+```
+
+## Attaching your engine to Leaf
+
+Leaf comes with a view manager, that makes Leaf aware of any template engine you want to use. To attach Smarty to Leaf, you need to call the `attach` method on the view manager. You can do this in your `index.php` file:
+
+```php
+assign('name', 'Ned');
+\Leaf\View::smarty()->display('index.tpl');
+```
+
+## Using with Leaf MVC
+
+When using Leaf MVC and Leaf API, you can attach Smarty to Leaf in your `public/index.php` file. This step is important because Leaf MVC and Leaf API come with a `view()` helper that you can use to render your templates. Registering Smarty with Leaf will make the `view()` helper aware of Smarty.
+
+```php
+\Leaf\View::attach(Smarty::class);
+```
+
+Now that you have Smarty attached to Leaf, all you need to do is head over to our view config file and replace the default template engine with Smarty. We can do this by changing the `engine` key in the `views` array to `smarty`:
+
+```php
+ Smarty::class,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom config method
+ |--------------------------------------------------------------------------
+ |
+ | Configuration for your templating engine.
+ |
+ */
+ 'config' => function ($config) {
+ View::smarty()->setTemplateDir($config['views']);
+ View::smarty()->setConfigDir('/some/config/dir');
+ View::smarty()->setCompileDir('/some/compile/dir');
+ View::smarty()->setCacheDir($config['cache']);
+ },
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom render method
+ |--------------------------------------------------------------------------
+ |
+ | This render method is triggered whenever render() is called
+ | in your app if you're using a custom view engine.
+ |
+ */
+ 'render' => function ($view, $data) {
+ foreach ($data as $key => $value) {
+ View::smarty()->assign($key, $value);
+ }
+
+ View::smarty()->display($view);
+ }),
+];
+```
+
+From the Smarty docs:
+
+> Smarty requires four directories which are by default named templates, configs, templates_c and cache relative to the current working directory.
+
+This is why we set the `setTemplateDir` and `setCacheDir` to the `views` and `cache` directories respectively.
+
+The function passed to the `render` key is called whenever the `render()` or `view()` helpers are called. This function is responsible for rendering the template. In this case, we are assigning the data passed to the `render()` or `view()` helper to the template and then displaying the template.
diff --git a/apps/docs/src/modules/views/veins/index.md b/apps/docs/src/modules/views/veins/index.md
new file mode 100755
index 0000000..c698d1c
--- /dev/null
+++ b/apps/docs/src/modules/views/veins/index.md
@@ -0,0 +1,213 @@
+# Leaf Veins
+
+
+
+
+
+Veins is a view engine shipped with Leaf v1. It has a perfect balance of simplicity and power as well as speed and flexibility. For those who have used **Smarty** before, this will be really easy to get used to.
+
+
+New to template engines?
+
+Watch this video by Dave Hollingworth as an introduction to template engines.
+
+
+
+
+## Installation
+
+To add veins to your project simply run the command:
+
+```bash
+leaf install veins
+```
+
+Or with composer:
+
+```bash
+composer require leafs/veins
+```
+
+## Usage with Leaf
+
+To use veins in a Leaf app, you need to attach veins to the Leaf view handler. This is done by adding the following code to your `app.php` file.
+
+```php
+Leaf\View::attach(Leaf\Veins::class);
+```
+
+From there, you can use the `veins` property on the `app` object to render your veins files.
+
+```php
+app()->veins->render('home');
+```
+
+## Usage without Leaf
+
+To use veins outside of a Leaf app, you need to initialize the `Leaf\Veins` class after installing it.
+
+```php
+$veins = new Leaf\Veins();
+```
+
+You can then call any of the methods on the `Leaf\Veins` class to render your veins files.
+
+```php
+$veins->render('home');
+```
+
+## Configuration
+
+Veins has a few configuration options that you can set. You can set these options using the `configure` method. The options are:
+
+- `charset` - The character set for your templates, default: `UTF-8`
+- `debug` - Show debug errors, default: `false`
+- `templateDir` - Directory to look for Vein templates, default: `views/`
+- `cacheDir` - Directory to hold compiled templates, default: `cache/`
+- `phpEnabled` - Whether to allow raw PHP in templates, default: `false`
+- `autoEscape` - Whether to sanitize variables or not, default: `true`
+- `removeComments` - Whether to html comments or not, default: `false`
+
+```php
+$app->veins->configure([
+ 'templateDir' => 'views/',
+ 'cacheDir' => 'views/cache/'
+]);
+```
+
+## Creating your views
+
+In the config above, we set the `templateDir` to `views/`. This means that Veins will look for all your views in the `views/` directory. So if we want to create a view called `homepage`, we'd create a file called `homepage.vein.html` in the `views/` directory.
+
+Note that Veins uses a `.vein.html` file extension for it's views.
+
+```html
+
+
+