diff --git a/.gitignore b/.gitignore index ecb7d9c..e289779 100644 --- a/.gitignore +++ b/.gitignore @@ -132,4 +132,4 @@ dist # custom generate.sh *.http -example* +example diff --git a/README.md b/README.md index 62aa295..649c875 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Fast, lightweight and zero dependency framework for [bunjs](https://bun.sh) 🚀 -![npm](https://img.shields.io/npm/v/colstonjs?color=blue&style=plastic) +![GitHub Workflow Status](https://img.shields.io/github/workflow/status/ajimae/colstonjs/publish?style=plastic) ![npm](https://img.shields.io/npm/v/colstonjs?color=blue&style=plastic) ![GitHub](https://img.shields.io/github/license/ajimae/colstonjs?style=plastic) ![npm](https://img.shields.io/npm/dt/colstonjs?style=plastic) @@ -13,21 +13,26 @@ Fast, lightweight and zero dependency framework for [bunjs](https://bun.sh) 🚀 - [Install](#installation) - [Usage](#usage) - [Examples](#examples) - - [Hellow Bun](#hello-bun) + - [Hello Bun](#hello-bun) - [Read request body as json or text](#read-request-body-as-json-or-text) - [Using named parameters](#using-named-parameters) - [Using query parameters](#using-query-parameters) - [Method Chaining](#method-chaining) + - [Running the demo note-app](#running-the-demo-note-app) - [Middleware](#middleware) - [Application-Level Middleware](#application-level-middleware) - [Route-Level Middleware](#route-level-middleware) + - [Context `locals`](#context-locals) + - [Router](#router) + - [Instantiating Router class](#instantiating-router-class) + - [Injecting Router instance into the app](#injecting-router-instance-into-the-app) - [Application instance cache](#application-instance-cache) - [Error Handler](#error-handler) - [Benchmark](#benchmark) - [Contribute](#contribute) - [License](#license) - [Author](#author) -- [Note](#note:) +- [DevNote](#devnote) ## Background @@ -198,6 +203,17 @@ app app.start(8000); ``` +#### Running the demo `note-app` +Follow the steps below to run the `demo note-taking api application` in the `examples`directory. +- Clone this repository +- Change directory into the note-app folder by running `cd examples/note-app` +- Start the http server to listen on port `8000` by running `bun app.js` +- User your favourite `http client` (e.g Postman) to make requests to the `listening http server`. + + + + + ### Middleware Colstonjs support both `route` level middleware as well as `app` level middleware. @@ -267,7 +283,71 @@ app.get("/", middleware-1, middleware-2, middleware-3, ..., middleware-k, (ctx: }); ... ``` +#### Context locals +`ctx.locals` is a plain javascript object that is specifically added to allow sharing of data amongst the chain of middlewares and/or handler functions. + +```ts +// server.ts +... +let requestCount = 0; +app.post("/request-count", (ctx, next) => { + /** + * req.locals can be used to pass + * data from one middleware to another + */ + ctx.locals.requestCount = requestCount; + next(); +}, (ctx, next) => { + ++ctx.locals.requestCount; + next(); +}, (ctx) => { + let count = ctx.locals.requestCount; + return ctx.status(200).text(count); // 1 +}); +``` + +### Router +#### Instantiating Router class +Router class provide a way to separate router specific declaration/blocks from the app logic, by providing that extra abstraction layer for your project. +```typescript +// router.ts +import Router from "Router"; + +// instantiate the router class +const router1 = new Router(); +const router2 = new Router(); + +// define user routes - can be in a separate file or module. +router1.post('/user', (ctx) => { return ctx.status(200).json({ user }) }); +router1.get('/users', (ctx) => { return ctx.json({ users }) }); +router1.delete('/user?id', (ctx) => { return ctx.status(204).head() }); + +// define the notes route - can also be in separate module. +router2.get('/note/:id', (ctx) => { return ctx.json({ note }) }); +router2.get('/notes', (ctx) => { return ctx.json({ notes }) }); +router2.post('/note', (ctx) => { return ctx.status(201).json({ note }) }); + +export { router1, router2 }; +``` + +#### Injecting Router instance into the app +```typescript +// server.ts +import Colston from "colstonjs"; +import { router1, router2 } from "./router"; + +const app: Colston = new Colston(); +app.all(router1, router2); + +// other routes can still be defined here +app.get("/", (ctx) => { + return ctx.status(200).text("Welcome to colstonjs framework for bun"); +}); + +app.start(8000) +``` +The `app.all()` method takes in k numbers of router instance objects e.g `app.all(router-1, router-2, ..., router-k);`. The [example](example) folder contains a full note taking backend app that utilizes this pattern. ## Application instance cache We can cache simple data which will leave throughout the application instance lifecycle. @@ -445,5 +525,5 @@ See the TODO doc [here](todo.md), feel free to also add to the list by editing t ## Author Coded with 💙 by [Chukwuemeka Ajima](https://github.com/ajimae) -## Note: +## DevNote: Although this version is fairly stable, it is actively still under development so also is [bunjs](https://bun.sh) and might contain some bugs, hence, not ideal for a production app. diff --git a/clean-dist b/clean-dist index 589dbea..fd058ae 100755 --- a/clean-dist +++ b/clean-dist @@ -11,12 +11,7 @@ cat << EOF > dist/index.d.ts import Colston from "./declarations/colston"; export default Colston; export * from "./declarations/types.d"; -EOF - -# update imports in index.js -cat << EOF > dist/index.js - import Colston from "./src/colston"; - export default Colston; + export { default as Router } from "./src/router"; EOF echo "✨ Done" diff --git a/examples/note-app/app.js b/examples/note-app/app.js new file mode 100644 index 0000000..66b68ed --- /dev/null +++ b/examples/note-app/app.js @@ -0,0 +1,63 @@ +import Colston from "../../index"; +import router from "./routes"; +import { logger, requestID } from "./middleware"; + +const app = new Colston(); + +let requestCount = 0; + +app + .get("/one", (ctx) => { + requestCount++; + return ctx.status(200).text("One"); + }) + .get("/two", ctx => ctx.text("two")) + .post("/two", (ctx) => { + requestCount++; + return ctx.status(200).text("Two"); + }) + .patch("/three", (ctx) => { + requestCount++; + return ctx.status(200).text("Three"); + }); + +app.post("/requestCount", (ctx, next) => { + /** + * req.locals can be used to pass + * data from one middleware to another + */ + ctx.locals.requestCount = requestCount; + next(); +}, (ctx, next) => { + ++ctx.locals.requestCount; + next(); +}, (ctx) => { + return ctx.status(200).text(ctx.locals.requestCount.toString()); +}); + +app.get("/request-id", requestID, (ctx) => { + return ctx.status(200).json({ + message: "This will give every request a unique ID and in the header too.", + requestID: ctx.request.id + }); +}); + +/** + * the app.all(...route: Router) mehtod + * accepts k-numbers of router instance objects + * where each router instance object are + * @example + * + * router-1 = new Router().get(path, ...middlewares) + * router-2 = new Router().post(path, ...niddlewares) + * ... + * router-k = new Router().(path, ...middlewares) + * + * app.all(router-1, router-2, ..., router-k) + */ +app.use(logger); +app.all(router); + +app.start(8000, function () { + console.log(`server running on port {8000}`); +}); diff --git a/examples/note-app/controller/NoteController.js b/examples/note-app/controller/NoteController.js new file mode 100644 index 0000000..1eda446 --- /dev/null +++ b/examples/note-app/controller/NoteController.js @@ -0,0 +1,65 @@ +/** + * NoteController class + * @class NoteController + * @methods getNotes + * @methods postNotes + */ +class NoteController { + constructor(noteRepository) { + this.noteRepository = noteRepository; + } + + /** + * retrieve all available notes + * @param {*} ctx + * @returns bun Response instance + */ + async getNotes(ctx) { + const notes = await this.noteRepository.getNotes(ctx); + + return ctx.status(200).json({ + success: true, + data: notes + }); + } + + /** + * retrieve all available notes + * @param {*} ctx + * @returns bun Response instance + */ + async getAllNotes(ctx) { + const notes = await this.noteRepository.getAllNotes(); + + return ctx.status(200).json({ + success: true, + data: notes + }); + } + + /** + * post a single note data + * @param {*} ctx + * @returns bun Response instance + */ + async postNote(ctx) { + const notes = await this.noteRepository.postNote(ctx); + + return ctx.status(201).json({ + success: true, + data: notes + }); + } + + /** + * delte a sinlge note data + * @param {*} ctx + * @returns bun Response instance + */ + async deleteNote(ctx) { + await this.noteRepository.deleteNote(ctx); + return ctx.status(204).head(); + } +} + +export default NoteController diff --git a/examples/note-app/controller/index.js b/examples/note-app/controller/index.js new file mode 100644 index 0000000..17f2ec8 --- /dev/null +++ b/examples/note-app/controller/index.js @@ -0,0 +1,2 @@ +import NoteController from "./NoteController"; +export default NoteController; diff --git a/examples/note-app/dataStore/data.json b/examples/note-app/dataStore/data.json new file mode 100644 index 0000000..df5da66 --- /dev/null +++ b/examples/note-app/dataStore/data.json @@ -0,0 +1,20 @@ +[ + { + "id": "L4JPI2tLmXMpwmaPcUZZlfuLljW7JYPP2hfmYcmSQ", + "note": "this is my first note", + "createdAt": "1970-01-01T00:00:00.005Z", + "updatedAt": "1970-01-01T00:00:00.005Z" + }, + { + "id": "jyBuT1zpMLc0uIQGDOKNkJJmI9YCzg5LLWU2OgkgE", + "note": "this is my second note", + "createdAt": "1980-01-01T00:00:00.035Z", + "updatedAt": "1980-01-01T00:00:00.035Z" + }, + { + "id": "QbSMatSi9YVxKQBQny9yBfAh8E3VWkkWg8GMHwQHdOA", + "note": "this is my third note", + "createdAt": "1990-01-01T00:00:02.000Z", + "updatedAt": "1990-01-01T00:00:02.000Z" + } +] diff --git a/examples/note-app/middleware/index.js b/examples/note-app/middleware/index.js new file mode 100644 index 0000000..e543579 --- /dev/null +++ b/examples/note-app/middleware/index.js @@ -0,0 +1,2 @@ +export { logger } from "./logger"; +export { requestID } from './requestID'; diff --git a/examples/note-app/middleware/logger.js b/examples/note-app/middleware/logger.js new file mode 100644 index 0000000..a4a9678 --- /dev/null +++ b/examples/note-app/middleware/logger.js @@ -0,0 +1,4 @@ +export const logger = (ctx) => { + const { pathname } = new URL(ctx.request.url); + console.info("- - " + [new Date()], "- - " + ctx.request.method + " " + pathname + " HTTP 1.1" + " - "); +} diff --git a/examples/note-app/middleware/requestID.js b/examples/note-app/middleware/requestID.js new file mode 100644 index 0000000..4ceacf9 --- /dev/null +++ b/examples/note-app/middleware/requestID.js @@ -0,0 +1,6 @@ +import crypto from "crypto"; // built into bun + +export const requestID = (ctx) => { + ctx.request.id = crypto.randomBytes(18).toString('hex'); + ctx.setHeader('request-id', ctx.request.id); +} diff --git a/examples/note-app/models/index.js b/examples/note-app/models/index.js new file mode 100644 index 0000000..e59ebfa --- /dev/null +++ b/examples/note-app/models/index.js @@ -0,0 +1 @@ +export * from "./notes"; \ No newline at end of file diff --git a/examples/note-app/models/notes.js b/examples/note-app/models/notes.js new file mode 100644 index 0000000..da3d165 --- /dev/null +++ b/examples/note-app/models/notes.js @@ -0,0 +1,32 @@ +import crypto from 'crypto'; +const data = require('../dataStore/data.json') + +export function find(id) { + return data.find((v) => v.id == id); +} + +export function findAll() { + return data; +} + +export function save(datum) { + const id = crypto. + randomBytes(32) + .toString('base64') + .replace(/[_+=\/]/gi, ''); + + const _data = { + id, + ...datum, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + } + + data.push(_data); + return _data; +} + +export function Delete(id) { + const idx = data.findIndex(v => v.id == id); + return data.splice(idx, 1); +} diff --git a/examples/note-app/repository/NoteRepository.js b/examples/note-app/repository/NoteRepository.js new file mode 100644 index 0000000..d1c0184 --- /dev/null +++ b/examples/note-app/repository/NoteRepository.js @@ -0,0 +1,49 @@ +/** + * NoteRepository class + * @class NotRepositorye + * @methods getNotes + * @methods postNote + */ +class NoteRepository { + constructor(noteModels) { + this.noteModels = noteModels; + } + + /** + * retrieve all available notes + * @param {*} ctx + * @returns bun Response instance + */ + async getNotes(ctx) { + const { id } = await ctx.request.params; + return this.noteModels.find(id) ?? {}; + } + + async getAllNotes() { + return this.noteModels.findAll(); + } + + /** + * post a single note data + * @param {*} ctx + * @returns bun Response instance + */ + async postNote(ctx) { + const note = await ctx.request.body; + const notes = await this.noteModels.save(note); + + return notes; + } + + /** + * delete a single note data + * @param {*} ctx + * @returns bun Reponse instance + */ + async deleteNote(ctx) { + const { id } = await ctx.request.query; + return this.noteModels.Delete(id); + } +} + +export default NoteRepository; diff --git a/examples/note-app/repository/index.js b/examples/note-app/repository/index.js new file mode 100644 index 0000000..3451a97 --- /dev/null +++ b/examples/note-app/repository/index.js @@ -0,0 +1,2 @@ +import NoteRepository from "./NoteRepository"; +export default NoteRepository; diff --git a/examples/note-app/routes/index.ts b/examples/note-app/routes/index.ts new file mode 100644 index 0000000..b86ca5e --- /dev/null +++ b/examples/note-app/routes/index.ts @@ -0,0 +1,2 @@ +import router from './notes'; +export default router; diff --git a/examples/note-app/routes/notes.js b/examples/note-app/routes/notes.js new file mode 100644 index 0000000..4284f8b --- /dev/null +++ b/examples/note-app/routes/notes.js @@ -0,0 +1,15 @@ +import { Router } from '../../../index'; +import * as model from '../models'; +import NoteRepository from '../repository'; +import NoteController from '../controller'; + +const router = new Router(); +const noteRepository = new NoteRepository(model); +const noteController = new NoteController(noteRepository); + +router.get('/note/:id', noteController.getNotes.bind(noteController)); +router.get('/notes', noteController.getAllNotes.bind(noteController)); +router.post('/note', noteController.postNote.bind(noteController)); +router.delete('/?id', noteController.deleteNote.bind(noteController)); + +export default router; diff --git a/index.ts b/index.ts index 0de31df..41d26ce 100644 --- a/index.ts +++ b/index.ts @@ -1,4 +1,12 @@ import Colston from "./src/colston"; -export * from "./src/types.d" export default Colston; +/** + * uncomment (the exported types.d) + * only when you are sure you are + * importing the root `index.ts` file + * into your project else leave it commented. + * + * export * from './src/types.d' + */ +export { default as Router } from "./src/router"; diff --git a/package.json b/package.json index 8f3cd83..e28647c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "colstonjs", - "version": "0.1.0-beta.2", + "version": "0.1.0-beta.3", "author": "Chukwuemeka Ajima ", "repository": "https://github.com/ajimae/colstonjs.git", "main": "dist/index.js", diff --git a/postman.jpeg b/postman.jpeg new file mode 100644 index 0000000..117b03e Binary files /dev/null and b/postman.jpeg differ diff --git a/postman_collection.json b/postman_collection.json new file mode 100644 index 0000000..69f4020 --- /dev/null +++ b/postman_collection.json @@ -0,0 +1,151 @@ +{ + "info": { + "_postman_id": "b12fc21b-9fc8-4269-acc1-5be36f8b42c7", + "name": "colstonjs-note-api", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "4784559" + }, + "item": [ + { + "name": "Get a single note", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://127.0.0.1:8000/note/L4JPI2tLmXMpwmaPcUZZlfuLljW7JYPP2hfmYcmSQ", + "protocol": "http", + "host": [ + "127", + "0", + "0", + "1" + ], + "port": "8000", + "path": [ + "note", + "L4JPI2tLmXMpwmaPcUZZlfuLljW7JYPP2hfmYcmSQ" + ], + "query": [ + { + "key": "", + "value": "", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Post a single note", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"note\": \"this is my first test note\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://127.0.0.1:8000/note", + "protocol": "http", + "host": [ + "127", + "0", + "0", + "1" + ], + "port": "8000", + "path": [ + "note" + ] + } + }, + "response": [] + }, + { + "name": "Get all notes", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://127.0.0.1:8000/notes", + "protocol": "http", + "host": [ + "127", + "0", + "0", + "1" + ], + "port": "8000", + "path": [ + "notes" + ] + } + }, + "response": [] + }, + { + "name": "Delete single note", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "http://127.0.0.1:8000/?id=zjY5DDtC44ye6qN812pyY5kWXHguqxxZjksgNKvku3E", + "protocol": "http", + "host": [ + "127", + "0", + "0", + "1" + ], + "port": "8000", + "path": [ + "" + ], + "query": [ + { + "key": "id", + "value": "zjY5DDtC44ye6qN812pyY5kWXHguqxxZjksgNKvku3E" + } + ] + } + }, + "response": [] + }, + { + "name": "Request ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://127.0.0.1:8000/request-id", + "protocol": "http", + "host": [ + "127", + "0", + "0", + "1" + ], + "port": "8000", + "path": [ + "request-id" + ] + } + }, + "response": [] + }, + { + "name": "Request Count", + "request": { + "method": "GET", + "header": [] + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/src/colston.ts b/src/colston.ts index 8076c6d..6020a10 100644 --- a/src/colston.ts +++ b/src/colston.ts @@ -1,11 +1,12 @@ import { Errorlike, Serve, Server } from "bun"; -import type { Middleware, Options, IColston} from "./types.d"; +import type { Middleware, Options, IColston } from "./types.d"; import parse from "./params"; import queryParse from "./query"; import readBody from "./body"; import Context from "./context"; import routeRegister from "./routeRegister"; import compose from "./middlewares"; +import Router from "./router"; /** * @class Colston @@ -15,7 +16,7 @@ import compose from "./middlewares"; */ export default class Colston implements IColston { readonly options: Options = {}; - readonly routeTable: object = {}; + readonly routeTable: Array = []; readonly middleware: Array = []; readonly cache = new Map(); @@ -107,57 +108,64 @@ export default class Colston implements IColston { } /** - * + * @description HTTP DELETE method + * @param path + * @param cb + * @returns {this} */ public delete(path: string, ...cb: Array): Colston { - routeRegister(path, "DELETE", cb, this.routeTable) + routeRegister(path, "DELETE", cb, this.routeTable); return this; } /** * @description add level route - * @param {string} path - * @param {Function} handler + * @param {Array} callbacks */ public use(...cb: Array<(ctx: Context) => Response | Promise | void>): void { this.middleware.push(...cb); } + public all(...routes: Array): Colston { + for (let i = 0; i < routes.length; i++) + this.routeTable.push(...routes[i].routeTable); + + return this; + } + /** * @description bun fetch function * @param {Request} request bun request object * @returns {Response} bun response object */ - // @ts-ignore async fetch(request: Request): Promise { + // https://github.com/oven-sh/bun/issues/677 + if (!request.method) request.verb = 'DELETE'; const context = new Context(request); /** * invoke all app level middlewares */ this.middleware.forEach((cb, _) => { - if (typeof cb == "function") { - cb(context); - } + if (typeof cb == "function") cb(context); }); - + let exists: boolean = false; - let routes: Array = []; - - routes = Object.keys(this.routeTable); - - // temporal fix for "/" path matching all routes before it. - const index = routes.indexOf("/"); - if (index > -1) routes.push(routes.splice(index, 1)[0]); - + let routes: Array> = []; + + routes = this.routeTable.map(v => Object.keys(v)); + + const idx = routes.findIndex(v => v[0] == "/"); + if (idx > -1) routes.push(routes.splice(idx, 1)[0]); + for (let i = 0; i < routes.length; i++) { const route = routes[i]; - let parsedRoute = parse(route); - + let parsedRoute = parse(route[0]); + if ( new RegExp(parsedRoute).test(request.url) && - this.routeTable[route][request.method.toLowerCase()] + this.routeTable[i][route[0]]?.[request.method.toLowerCase() || request.verb.toLowerCase()] ) { - const middleware = this.routeTable[route][request.method.toLowerCase()]; + const middleware = this.routeTable[i][route[0]][request.method.toLowerCase() || request.verb.toLowerCase()]; const m = request.url.match(new RegExp(parsedRoute)); const _middleware = middleware.slice(); diff --git a/src/context.ts b/src/context.ts index f351d04..4f062eb 100644 --- a/src/context.ts +++ b/src/context.ts @@ -22,6 +22,11 @@ export default class Context { return this; } + public setHeader(key: string, value: any) { + this.headers[key] = value.toString(); + return this; + } + /** * @warning method might behave unexpectedly * @param raw diff --git a/src/params.ts b/src/params.ts index acc7e97..54d499a 100644 --- a/src/params.ts +++ b/src/params.ts @@ -29,17 +29,12 @@ function parse(url: string): string { } } - /** - * TODO: - * fix issue with route not matching exact value - */ if (isQuery) { return str; } - // add end border to query string str += "$"; - return str + return str; } export default parse; diff --git a/src/routeRegister.ts b/src/routeRegister.ts index 4657339..ed2a4d6 100644 --- a/src/routeRegister.ts +++ b/src/routeRegister.ts @@ -1,17 +1,16 @@ -import Context from "./context"; import { methods } from "./methods"; import type { Middleware, MethodType } from "./types.d"; -export default function register(path: string, method: MethodType, callback: Array, routeTable: object = {}): void | never { - routeTable[path] = validate(path, method, callback); +export default function register(path: string, method: MethodType, callback: Array, routeTable: Array = []): void | never { + routeTable.push({ [path]: validate(path, method, callback) }); } function validate(path: string, method: MethodType, callback: Array): { [path: string]: Array } { if (methods.indexOf(method) === -1) throw new Error("Invalid HTTP method, Accepted methods are: " + methods.join(" ")); if (path.charAt(0) !== "/") throw new Error("Invalid path, path must start with '/'"); - + for (const i in callback) - if (typeof callback[i] !== "function") throw new Error("Invalid handler function, handler must be a function"); - + if (typeof callback[i] !== "function") throw new Error("Invalid handler function, handler must be a function"); + return { [method.toLowerCase()]: callback }; } diff --git a/src/router.ts b/src/router.ts new file mode 100644 index 0000000..ee0b99b --- /dev/null +++ b/src/router.ts @@ -0,0 +1,58 @@ +import type { Middleware } from "./types.d"; +import routeRegister from "./routeRegister"; + +export default class Router { + routeTable: Array = []; + // constructor(routeTable?: RouteTable) { + // this.routeTable = routeTable; + // } + + /** + * @description register HTTP GET method + * @param path + * @returns void + */ + public get(path: string, ...cb: Array): any { + routeRegister(path, "GET", cb, this.routeTable); + } + + /** + * @description register HTTP POST method + * @param path + * @param cb + * @returns {this} + */ + public post(path: string, ...cb: Array): void { + routeRegister(path, "POST", cb, this.routeTable); + } + + /** + * @description register HTTP PATCH method + * @param path + * @param cb + * @returns {this} + */ + public patch(path: string, ...cb: Array): void { + routeRegister(path, "PATCH", cb, this.routeTable); + } + + /** + * @description register HTTP PUT method + * @param path + * @param cb + * @returns {this} + */ + public put(path: string, ...cb: Array): void { + routeRegister(path, "PUT", cb, this.routeTable); + } + + /** + * @description register HTTP DELETE method + * @param path + * @param cb + * @returns {this} + */ + public delete(path: string, ...cb: Array): void { + routeRegister(path, "DELETE", cb, this.routeTable); + } +} diff --git a/src/types.d.ts b/src/types.d.ts index 6542073..a96f102 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -5,6 +5,7 @@ declare global { query: Record; params: Record; body: object | string; + verb: string; } } export declare type Options = { @@ -26,7 +27,7 @@ export type MethodType = export declare type Context = _Context; export declare type Next = () => Promise | void; -export declare type Middleware = (context: Context, next: Next ) => Response | void | Promise; +export declare type Middleware = (context: Context, next: Next) => Response | void | Promise; /** * @class Jade * @description add route to routeTable, match and process request @@ -38,7 +39,7 @@ export interface IColston { readonly routeTable: object; readonly middleware: Array; readonly cache: Map; - + /** * @description internal error handler * @param error @@ -108,4 +109,4 @@ export interface IColston { * @returns bun server instance */ start(port?: number, cb?: Function): Server; -} \ No newline at end of file +} diff --git a/todo.md b/todo.md index 04e554f..ec4debb 100644 --- a/todo.md +++ b/todo.md @@ -5,5 +5,5 @@ - [x] strip out radix3 and ufo for custom router table and parser - [ ] allow middleware to return response and exit the application lifecycle at anytime. - [ ] add unit and integration tests -- [ ] add example folder with example(s) source file +- [x] add example folder with example(s) source file - [ ] rewrite the route implementation to allow wild cards and route paths (string) pattern matching diff --git a/tsconfig.json b/tsconfig.json index 5b5710b..c06481a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "target": "ES5", "declaration": true, "esModuleInterop": true, "isolatedModules": true,