Created
May 23, 2025 21:05
-
-
Save BashkaMen/a1111eef29f2526b926022d83ff0ebad to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { randomUUIDv7 } from 'bun'; | |
| import { Map, List, Set, set, } from 'immutable' | |
| import z from 'zod'; | |
| type HttpMethod = "GET" | "POST" | |
| type Request = { body: string; url: string; method: HttpMethod; headers: Map<string, string> } | |
| type Response = { body: string; url: string, method: HttpMethod; headers: Map<string, string> } | |
| type Context = { | |
| request: Request; | |
| response: Response; | |
| } | |
| type Middleware = (context: Context) => Promise<Context | null> | |
| export const skip : Middleware = (context) => Promise.resolve(null) | |
| export const next : Middleware = (context) => Promise.resolve(context) | |
| export const map_response = (fn: (response: Response) => Response) : Middleware => async (ctx) => { | |
| const reponse = fn(ctx.response) | |
| return { ...ctx, response: reponse } | |
| } | |
| export const set_header = (name: string, value: string) : Middleware => | |
| map_response(res => ({ ...res, headers: res.headers.set(name, value) })) | |
| export const set_body = (body: string) : Middleware => | |
| map_response(res => ({ ...res, body })) | |
| export const path = (path: string) : Middleware => (ctx) => { | |
| if (ctx.request.url === path) { | |
| return next(ctx) | |
| } | |
| return skip(ctx) | |
| } | |
| export const method = (method: HttpMethod) : Middleware => (ctx) => { | |
| if (ctx.request.method === method) { | |
| return next(ctx) | |
| } | |
| return skip(ctx) | |
| } | |
| export const compose = (ms: Middleware[]): Middleware => async (ctx) => { | |
| let current : Context | null = ctx | |
| for (const m of ms) { | |
| current = await m(current) | |
| if (!current) { | |
| return await skip(ctx) | |
| } | |
| } | |
| return await skip(ctx) | |
| } | |
| export const choose = (ms: Middleware[]) : Middleware => async (ctx) => { | |
| for (const m of ms) { | |
| const ctx1 = await m(ctx) | |
| if (ctx1) { | |
| return ctx1 | |
| } | |
| } | |
| return ctx | |
| } | |
| export const route = (http_method: HttpMethod, http_path: string) : Middleware => compose([ | |
| method(http_method), | |
| path(http_path), | |
| ]) | |
| export const set_text_body = (text: string) : Middleware => compose([ | |
| set_header("Content-Type", "text/plain"), | |
| set_body(text), | |
| ]) | |
| export const set_json_body = (json: any) : Middleware => set_json_body(JSON.stringify(json)) | |
| export const bind_json = <T>(fn: (item: T) => Middleware) : Middleware => async (ctx) => { | |
| const json = JSON.parse(ctx.request.body) | |
| return fn(json)(ctx) | |
| } | |
| export const typed_json_handler = | |
| <TReq, TRes>(opts: { | |
| handler: (req: TReq) => Promise<TRes>, | |
| validate?: (req: TReq) => void | |
| }) : Middleware => | |
| bind_json((req: TReq) => async (ctx) => { | |
| opts.validate?.(req) | |
| const res = await opts.handler(req) | |
| return set_json_body(res)(ctx) | |
| }) | |
| export const authorize = (roles: string[] = []) : Middleware => (ctx) => { | |
| const auth_header = ctx.request.headers.get("Authorization") | |
| // decode jwt | |
| if (roles.length === 0) { | |
| return next(ctx) | |
| } | |
| if (roles.some(role => auth_header?.includes(role))) { | |
| return next(ctx) | |
| } | |
| return skip(ctx) | |
| } | |
| type Endpoint<TReq, TRes> = { | |
| method: HttpMethod, | |
| path: string, | |
| handler: (item: TReq) => Promise<TRes> | |
| validate?: (item: TReq) => Promise<TRes> | |
| } | |
| const schema = z.object({ | |
| email: z.string().email(), | |
| }) | |
| type ZodEndpoint<TReq, TRes> = { | |
| method: HttpMethod, | |
| path: string, | |
| schema: z.Schema | |
| handler: (item: TReq) => Promise<TRes> | |
| } | |
| export const endpoint = <TReq, TRes>(endpoint: Endpoint<TReq, TRes>) : Middleware => (ctx) => { | |
| const item = JSON.parse(ctx.request.body) | |
| endpoint.validate?.(item) | |
| const res = endpoint.handler(item) | |
| return set_json_body(res)(ctx) | |
| } | |
| type CreateUserReq = { email: string } | |
| export const create_user = compose([ | |
| method("POST"), | |
| path("/user/:create"), | |
| bind_json((req: CreateUserReq) => async (ctx) =>{ | |
| const user = { id: randomUUIDv7(), email: req.email, created_at: new Date().valueOf() } | |
| //await db.insert(user) | |
| return ctx | |
| }) | |
| ]) | |
| type CreateOrder = { type: string } | |
| type CreateOrderReponse = { type: string } | |
| export const create_order = compose([ | |
| method("POST"), | |
| path("/user/:create"), | |
| authorize(), | |
| typed_json_handler<CreateOrder, CreateOrderReponse>({ | |
| handler: async (req: CreateOrder) => { | |
| return { type: req.type} | |
| } | |
| }) | |
| ]) | |
| const create_user_endpoint : Endpoint<CreateUserReq, void> = { | |
| method: "POST", | |
| path: "/user/:create", | |
| handler: async (req: CreateUserReq) => { | |
| const user = { id: randomUUIDv7(), email: req.email, created_at: new Date().valueOf() } | |
| //await db.insert(user) | |
| return; | |
| } | |
| } | |
| const app = compose([ | |
| set_header("Server", "fs-server"), | |
| choose([ | |
| compose([ path("/"), method("GET"), set_header("Content-Type", "text/html"), set_body("<h1>Home</h1>") ]), | |
| compose([route("GET", "/hello"), set_header("Content-Type", "text/html"), set_body("Hello World")]), | |
| create_user, | |
| create_order, | |
| endpoint(create_user_endpoint), | |
| ]) | |
| ]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment