{"id":46092669,"url":"https://github.com/matthewthecoder1218/princejs","last_synced_at":"2026-04-01T23:27:49.098Z","repository":{"id":323312630,"uuid":"1092821281","full_name":"MatthewTheCoder1218/princejs","owner":"MatthewTheCoder1218","description":"The smallest backend framework and among the top three in the world.","archived":false,"fork":false,"pushed_at":"2026-02-26T15:56:12.000Z","size":204,"stargazers_count":173,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-26T21:56:33.348Z","etag":null,"topics":["api","backend","bun","database","fast","framework","jose","jwt","lightweight","middleware","princejs","rest","scheduler","server","sse","typescript","websocket","zod"],"latest_commit_sha":null,"homepage":"https://princejs.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MatthewTheCoder1218.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-09T11:36:39.000Z","updated_at":"2026-02-26T15:57:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/MatthewTheCoder1218/princejs","commit_stats":null,"previous_names":["matthewthecoder1218/princejs"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/MatthewTheCoder1218/princejs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MatthewTheCoder1218%2Fprincejs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MatthewTheCoder1218%2Fprincejs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MatthewTheCoder1218%2Fprincejs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MatthewTheCoder1218%2Fprincejs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MatthewTheCoder1218","download_url":"https://codeload.github.com/MatthewTheCoder1218/princejs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MatthewTheCoder1218%2Fprincejs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29977966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T16:35:47.903Z","status":"ssl_error","status_checked_at":"2026-03-01T16:35:44.899Z","response_time":124,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","backend","bun","database","fast","framework","jose","jwt","lightweight","middleware","princejs","rest","scheduler","server","sse","typescript","websocket","zod"],"created_at":"2026-03-01T18:01:02.588Z","updated_at":"2026-04-01T23:27:49.091Z","avatar_url":"https://github.com/MatthewTheCoder1218.png","language":"TypeScript","readme":"\u003cdiv align=\"center\"\u003e\n\n# 👑 PrinceJS\n\n**Ultra-clean, modern \u0026 minimal Bun web framework.**  \nBuilt by a 13-year-old Nigerian developer. Among the top three in performance.\n\n[![npm version](https://img.shields.io/npm/v/princejs?style=flat-square)](https://www.npmjs.com/package/princejs)\n[![GitHub stars](https://img.shields.io/github/stars/MatthewTheCoder1218/princejs?style=flat-square)](https://github.com/MatthewTheCoder1218/princejs)\n[![npm downloads](https://img.shields.io/npm/dt/princejs?style=flat-square)](https://www.npmjs.com/package/princejs)\n[![license](https://img.shields.io/github/license/MatthewTheCoder1218/princejs?style=flat-square)](https://github.com/MatthewTheCoder1218/princejs/blob/main/LICENSE)\n\n[**Website**](https://princejs.vercel.app) · [**npm**](https://www.npmjs.com/package/princejs) · [**GitHub**](https://github.com/MatthewTheCoder1218/princejs) · [**Twitter**](https://twitter.com/princejs_bun)\n\n\u003c/div\u003e\n\n---\n\n## ⚡ Performance\n\nBenchmarked with `oha -c 100 -z 30s` on Windows 10:\n\n| Framework | Avg Req/s | Peak Req/s |\n|-----------|----------:|-----------:|\n| Elysia | 27,606 | 27,834 |\n| **PrinceJS** | **17,985** | **18,507** |\n| Hono | 17,914 | 18,826 |\n| Fastify | 15,519 | 16,434 |\n| Express | 13,138 | 13,458 |\n\n\u003e PrinceJS is **2.3× faster than Express**, matches Hono head-to-head, and sits at approximately 5kB gzipped — loads in approximately 100ms on a slow 3G connection.\n\n---\n\n## 🚀 Quick Start\n\n```bash\nbun add princejs\n# or\nnpm install princejs\n```\n\n```ts\nimport { prince } from \"princejs\";\nimport { cors, logger } from \"princejs/middleware\";\n\nconst app = prince();\n\napp.use(cors());\napp.use(logger());\n\napp.get(\"/\", () =\u003e ({ message: \"Hello PrinceJS!\" }));\napp.get(\"/users/:id\", (req) =\u003e ({ id: req.params?.id }));\n\napp.listen(3000);\n```\n\n---\n\n## 🧰 Features\n\n| Feature | Import |\n|---------|--------|\n| Routing, Route Grouping, WebSockets, OpenAPI, Plugins, Lifecycle Hooks, Cookies, IP | `princejs` |\n| CORS, Logger, JWT, JWKS, Auth, Rate Limit, Validate, Compress, Session, API Key, Secure Headers, Timeout, Request ID, IP Restriction, Static Files, Trim Trailing Slash, Middleware Combinators (`every`, `some`, `except`), `guard()` | `princejs/middleware` |\n| File Uploads, SSE, Streaming, In-memory Cache | `princejs/helpers` |\n| Cron Scheduler | `princejs/scheduler` |\n| JSX / SSR | `princejs/jsx` |\n| SQLite Database | `princejs/db` |\n| End-to-End Type Safety | `princejs/client` |\n| Vercel Edge adapter | `princejs/vercel` |\n| Cloudflare Workers adapter | `princejs/cloudflare` |\n| Deno Deploy adapter | `princejs/deno` |\n| Node.js / Express adapter | `princejs/node` |\n\n---\n\n## 🍪 Cookies \u0026 🌐 IP Detection\n\n### Reading Cookies\n\nCookies are automatically parsed and available on every request:\n\n```ts\nimport { prince } from \"princejs\";\n\nconst app = prince();\n\napp.get(\"/profile\", (req) =\u003e ({\n  sessionId: req.cookies?.sessionId,\n  theme: req.cookies?.theme,\n  allCookies: req.cookies, // Record\u003cstring, string\u003e\n}));\n```\n\n### Setting Cookies\n\nUse the response builder for full cookie control:\n\n```ts\napp.get(\"/login\", (req) =\u003e\n  app.response()\n    .status(200)\n    .json({ ok: true })\n    .cookie(\"sessionId\", \"abc123\", {\n      maxAge: 3600,       // 1 hour\n      path: \"/\",\n      httpOnly: true,     // not accessible from JS\n      secure: true,       // HTTPS only\n      sameSite: \"Strict\", // CSRF protection\n    })\n);\n\n// Chain multiple cookies\napp.response()\n  .json({ ok: true })\n  .cookie(\"session\", \"xyz\")\n  .cookie(\"theme\", \"dark\")\n  .cookie(\"lang\", \"en\");\n```\n\n### Client IP Detection\n\n```ts\napp.get(\"/api/data\", (req) =\u003e ({\n  clientIp: req.ip,\n  data: [],\n}));\n```\n\n**Supported headers** (in priority order):\n- `X-Forwarded-For` — load balancers, proxies (first IP in list)\n- `X-Real-IP` — Nginx, Apache reverse proxy\n- `CF-Connecting-IP` — Cloudflare\n- `X-Client-IP` — other proxy services\n- Fallback — `127.0.0.1`\n\n```ts\n// IP-based rate limiting\napp.use((req, next) =\u003e {\n  const count = ipTracker.getCount(req.ip) || 0;\n  if (count \u003e 100) return new Response(\"Too many requests\", { status: 429 });\n  ipTracker.increment(req.ip);\n  return next();\n});\n\n// IP allowlist\napp.post(\"/admin\", (req) =\u003e {\n  if (!ALLOWED_IPS.includes(req.ip!)) {\n    return new Response(\"Forbidden\", { status: 403 });\n  }\n  return { authorized: true };\n});\n```\n\n---\n\n\n## 🗂️ Route Grouping\n\nGroup routes under a shared prefix with optional shared middleware. Zero overhead at request time — purely a registration convenience.\n\n```ts\nimport { prince } from \"princejs\";\n\nconst app = prince();\n\n// Basic grouping\napp.group(\"/api\", (r) =\u003e {\n  r.get(\"/users\",     () =\u003e ({ users: [] }));\n  r.post(\"/users\",    (req) =\u003e ({ created: req.parsedBody }));\n  r.get(\"/users/:id\", (req) =\u003e ({ id: req.params?.id }));\n});\n// → GET  /api/users\n// → POST /api/users\n// → GET  /api/users/:id\n\n// With shared middleware — applies to every route in the group\nimport { auth } from \"princejs/middleware\";\n\napp.group(\"/admin\", auth(), (r) =\u003e {\n  r.get(\"/stats\",   () =\u003e ({ stats: {} }));\n  r.delete(\"/users/:id\", (req) =\u003e ({ deleted: req.params?.id }));\n});\n\n// Chainable\napp\n  .group(\"/v1\", (r) =\u003e { r.get(\"/ping\", () =\u003e ({ v: 1 })); })\n  .group(\"/v2\", (r) =\u003e { r.get(\"/ping\", () =\u003e ({ v: 2 })); });\n\napp.listen(3000);\n```\n\n---\n\n## 🛡️ Secure Headers\n\nOne call sets all the security headers your production app needs:\n\n```ts\nimport { secureHeaders } from \"princejs/middleware\";\n\napp.use(secureHeaders());\n// Sets: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection,\n//       Strict-Transport-Security, Referrer-Policy\n\n// Custom options\napp.use(secureHeaders({\n  xFrameOptions: \"DENY\",\n  contentSecurityPolicy: \"default-src 'self'\",\n  permissionsPolicy: \"camera=(), microphone=()\",\n  strictTransportSecurity: \"max-age=63072000; includeSubDomains; preload\",\n}));\n```\n\n---\n\n## ⏱️ Request Timeout\n\nKill hanging requests before they pile up:\n\n```ts\nimport { timeout } from \"princejs/middleware\";\n\napp.use(timeout(5000));          // 5 second global timeout → 408\napp.use(timeout(3000, \"Slow!\")); // custom message\n\n// Per-route timeout\napp.get(\"/heavy\", timeout(10000), (req) =\u003e heavyOperation());\n```\n\n---\n\n## 🏷️ Request ID\n\nAttach a unique ID to every request for distributed tracing and log correlation:\n\n```ts\nimport { requestId } from \"princejs/middleware\";\n\napp.use(requestId());\n// → sets req.id and X-Request-ID response header\n\n// Custom header name\napp.use(requestId({ header: \"X-Trace-ID\" }));\n\n// Custom generator\napp.use(requestId({ generator: () =\u003e `req-${Date.now()}` }));\n\napp.get(\"/\", (req) =\u003e ({ requestId: req.id }));\n```\n\n---\n\n## 🚫 IP Restriction\n\nAllow or block specific IPs:\n\n```ts\nimport { ipRestriction } from \"princejs/middleware\";\n\n// Only allow these IPs\napp.use(ipRestriction({ allowList: [\"192.168.1.1\", \"10.0.0.1\"] }));\n\n// Block these IPs\napp.use(ipRestriction({ denyList: [\"1.2.3.4\"] }));\n```\n\n---\n\n## ✂️ Trim Trailing Slash\n\nAutomatically redirect `/users/` → `/users` so you never get mysterious 404s from a stray trailing slash:\n\n```ts\nimport { trimTrailingSlash } from \"princejs/middleware\";\n\napp.use(trimTrailingSlash());        // 301 by default\napp.use(trimTrailingSlash(302));     // or 302 temporary redirect\n```\n\nRoot `/` is never redirected. Query strings are preserved — `/search/?q=bun` → `/search?q=bun`.\n\n---\n\n## 🔀 Middleware Combinators\n\nCompose complex auth rules in a single readable line.\n\n### `every()` — all must pass\n\n```ts\nimport { every } from \"princejs/middleware\";\n\nconst isAdmin = async (req, next) =\u003e {\n  if (req.user?.role !== \"admin\")\n    return new Response(JSON.stringify({ error: \"Forbidden\" }), { status: 403 });\n  return next();\n};\n\napp.get(\"/admin\", every(auth(), isAdmin), () =\u003e ({ ok: true }));\n// short-circuits on first rejection — isAdmin never runs if auth() fails\n```\n\n### `some()` — either must pass\n\n```ts\nimport { some } from \"princejs/middleware\";\n\n// Accept a JWT token OR an API key — whichever the client sends\napp.get(\"/resource\", some(auth(), apiKey({ keys: [\"key_123\"] })), () =\u003e ({ ok: true }));\n```\n\n### `except()` — skip middleware for certain paths\n\n```ts\nimport { except } from \"princejs/middleware\";\n\n// Apply auth everywhere except /health and /\napp.use(except([\"/health\", \"/\"], auth()));\n\napp.get(\"/health\", () =\u003e ({ ok: true }));   // no auth\napp.get(\"/private\", (req) =\u003e ({ user: req.user })); // auth required\n```\n\n---\n\n## 🛡️ guard()\n\nApply a validation schema to every route in a group at once — no need to repeat `validate()` on each handler:\n\n```ts\nimport { guard } from \"princejs/middleware\";\nimport { z } from \"zod\";\n\napp.group(\"/users\", guard({ body: z.object({ name: z.string().min(1) }) }), (r) =\u003e {\n  r.post(\"/\",      (req) =\u003e ({ created: req.parsedBody.name })); // auto-validated\n  r.put(\"/:id\",    (req) =\u003e ({ updated: req.parsedBody.name })); // auto-validated\n});\n// Bad body → 400 { error: \"Validation failed\", details: [...] }\n```\n\nAlso works as standalone route middleware:\n\n```ts\napp.post(\n  \"/items\",\n  guard({ body: z.object({ name: z.string(), price: z.number() }) }),\n  (req) =\u003e ({ created: req.parsedBody })\n);\n```\n\n---\n\n## 📁 Static Files\n\nServe a directory of static files. Falls through to your routes if the file doesn't exist:\n\n```ts\nimport { serveStatic } from \"princejs/middleware\";\n\napp.use(serveStatic(\"./public\"));\n// → GET /logo.png        serves ./public/logo.png\n// → GET /               serves ./public/index.html\n// → GET /api/users      falls through to your route handler\n```\n\n---\n\n## 🌊 Streaming\n\nStream chunked responses for AI/LLM output, large payloads, or anything that generates data over time:\n\n```ts\nimport { stream } from \"princejs/helpers\";\n\n// Async generator — cleanest for AI token streaming\napp.get(\"/ai\", stream(async function*(req) {\n  yield \"Hello \";\n  await delay(100);\n  yield \"from \";\n  yield \"PrinceJS!\";\n}));\n\n// Async callback\napp.get(\"/data\", stream(async (req) =\u003e {\n  req.streamSend(\"chunk 1\");\n  await fetchMoreData();\n  req.streamSend(\"chunk 2\");\n}));\n\n// Custom content type for binary or JSON streams\napp.get(\"/events\", stream(async function*(req) {\n  for (const item of items) {\n    yield JSON.stringify(item) + \"\\n\";\n  }\n}, { contentType: \"application/x-ndjson\" }));\n```\n\n---\n\n## 🔑 JWKS / Third-Party Auth\n\nVerify JWTs from Auth0, Clerk, Supabase, or any JWKS endpoint — no symmetric key needed:\n\n```ts\nimport { jwks } from \"princejs/middleware\";\n\n// Auth0\napp.use(jwks(\"https://your-domain.auth0.com/.well-known/jwks.json\"));\n\n// Clerk\napp.use(jwks(\"https://your-clerk-domain.clerk.accounts.dev/.well-known/jwks.json\"));\n\n// Supabase\napp.use(jwks(\"https://your-project.supabase.co/auth/v1/.well-known/jwks.json\"));\n\n// req.user is set after verification, same as jwt()\napp.get(\"/protected\", auth(), (req) =\u003e ({ user: req.user }));\n```\n\n---\n\n## 📖 OpenAPI + Scalar Docs ✨\n\nAuto-generate an OpenAPI 3.0 spec and serve a beautiful [Scalar](https://scalar.com) UI — all from a single `app.openapi()` call.\n\n```ts\nimport { prince } from \"princejs\";\nimport { z } from \"zod\";\n\nconst app = prince();\n\nconst api = app.openapi({ title: \"My API\", version: \"1.0.0\" }, \"/docs\", { theme: \"moon\" });\n\napi.route(\"GET\", \"/users/:id\", {\n  summary: \"Get user by ID\",\n  tags: [\"users\"],\n  schema: {\n    response: z.object({ id: z.string(), name: z.string() }),\n  },\n}, (req) =\u003e ({ id: req.params!.id, name: \"Alice\" }));\n\napi.route(\"POST\", \"/users\", {\n  summary: \"Create user\",\n  tags: [\"users\"],\n  schema: {\n    body:     z.object({ name: z.string().min(2), email: z.string().email() }),\n    response: z.object({ id: z.string(), name: z.string(), email: z.string() }),\n  },\n}, (req) =\u003e ({ id: crypto.randomUUID(), ...req.parsedBody }));\n\napp.listen(3000);\n// → GET /docs       Scalar UI\n// → GET /docs.json  Raw OpenAPI JSON\n```\n\n`api.route()` does three things at once:\n\n- ✅ Registers the route on PrinceJS\n- ✅ Auto-wires body validation — no separate middleware needed\n- ✅ Writes the full OpenAPI spec entry\n\n| `schema` key | Runtime effect | Scalar docs |\n|---|---|---|\n| `body` | ✅ Validates \u0026 rejects bad requests | ✅ requestBody model |\n| `query` | — | ✅ Typed query params |\n| `response` | — | ✅ 200 response model |\n\n\u003e Routes on `app.get()` / `app.post()` stay private — they never appear in the docs.\n\n**Themes:** `default` · `moon` · `purple` · `solarized` · `bluePlanet` · `deepSpace` · `saturn` · `kepler` · `mars`\n\n---\n\n## 🔌 Plugin System\n\n```ts\nimport { prince, type PrincePlugin } from \"princejs\";\n\nconst usersPlugin: PrincePlugin\u003c{ prefix?: string }\u003e = (app, opts) =\u003e {\n  const base = opts?.prefix ?? \"\";\n\n  app.use((req, next) =\u003e {\n    (req as any).fromPlugin = true;\n    return next();\n  });\n\n  app.get(`${base}/users`, (req) =\u003e ({\n    ok: true,\n    fromPlugin: (req as any).fromPlugin,\n  }));\n};\n\nconst app = prince();\napp.plugin(usersPlugin, { prefix: \"/api\" });\napp.listen(3000);\n```\n\n---\n\n## 🎣 Lifecycle Hooks\n\n```ts\nimport { prince } from \"princejs\";\n\nconst app = prince();\n\napp.onRequest((req) =\u003e {\n  (req as any).startTime = Date.now();\n});\n\napp.onBeforeHandle((req, path, method) =\u003e {\n  console.log(`🔍 ${method} ${path}`);\n});\n\napp.onAfterHandle((req, res, path, method) =\u003e {\n  const ms = Date.now() - (req as any).startTime;\n  console.log(`✅ ${method} ${path} ${res.status} (${ms}ms)`);\n});\n\napp.onError((err, req, path, method) =\u003e {\n  console.error(`❌ ${method} ${path}:`, err.message);\n});\n\napp.get(\"/users\", () =\u003e ({ users: [] }));\napp.listen(3000);\n```\n\n**Execution order:**\n1. `onRequest` — runs before routing, good for setup\n2. `onBeforeHandle` — just before the handler\n3. Handler executes\n4. `onAfterHandle` — after success (skipped on error)\n5. `onError` — only when handler throws\n\n---\n\n## 🔒 End-to-End Type Safety\n\n```ts\nimport { createClient, type PrinceApiContract } from \"princejs/client\";\n\ntype ApiContract = {\n  \"GET /users/:id\": {\n    params: { id: string };\n    response: { id: string; name: string };\n  };\n  \"POST /users\": {\n    body: { name: string };\n    response: { id: string; ok: boolean };\n  };\n};\n\nconst client = createClient\u003cApiContract\u003e(\"http://localhost:3000\");\n\nconst user = await client.get(\"/users/:id\", { params: { id: \"42\" } });\nconsole.log(user.name); // typed as string ✅\n\nconst created = await client.post(\"/users\", { body: { name: \"Alice\" } });\nconsole.log(created.id); // typed as string ✅\n```\n\n---\n\n## 🌍 Deploy Adapters\n\n**Vercel Edge** — `api/[[...route]].ts`\n```ts\nimport { toVercel } from \"princejs/vercel\";\nexport default toVercel(app);\n```\n\n**Cloudflare Workers** — `src/index.ts`\n```ts\nimport { toWorkers } from \"princejs/cloudflare\";\nexport default toWorkers(app);\n```\n\n**Deno Deploy** — `main.ts`\n```ts\nimport { toDeno } from \"princejs/deno\";\nDeno.serve(toDeno(app));\n```\n\n**Node.js** — `server.ts`\n```ts\nimport { createServer } from \"http\";\nimport { toNode, toExpress } from \"princejs/node\";\nimport express from \"express\";\n\nconst app = prince();\napp.get(\"/\", () =\u003e ({ message: \"Hello!\" }));\n\n// Native Node http\ncreateServer(toNode(app)).listen(3000);\n\n// Or drop into Express\nconst expressApp = express();\nexpressApp.all(\"*\", toExpress(app));\nexpressApp.listen(3000);\n```\n\n---\n\n## 🎯 Full Example\n\n```ts\nimport { prince } from \"princejs\";\nimport {\n  cors,\n  logger,\n  rateLimit,\n  auth,\n  apiKey,\n  jwt,\n  signJWT,\n  session,\n  compress,\n  validate,\n  secureHeaders,\n  timeout,\n  requestId,\n  trimTrailingSlash,\n  every,\n  some,\n  except,\n  guard,\n} from \"princejs/middleware\";\nimport { cache, upload, sse, stream } from \"princejs/helpers\";\nimport { cron } from \"princejs/scheduler\";\nimport { Html, Head, Body, H1, P, render } from \"princejs/jsx\";\nimport { db } from \"princejs/db\";\nimport { z } from \"zod\";\n\nconst SECRET = new TextEncoder().encode(\"your-secret\");\nconst app = prince();\n\n// ── Lifecycle hooks ───────────────────────────────────────\napp.onRequest((req) =\u003e { (req as any).t = Date.now(); });\napp.onAfterHandle((req, res, path, method) =\u003e {\n  console.log(`✅ ${method} ${path} ${res.status} (${Date.now() - (req as any).t}ms)`);\n});\napp.onError((err, req, path, method) =\u003e {\n  console.error(`❌ ${method} ${path}:`, err.message);\n});\n\n// ── Global middleware ─────────────────────────────────────\napp.use(secureHeaders());\napp.use(requestId());\napp.use(trimTrailingSlash());\napp.use(timeout(10000));\napp.use(cors());\napp.use(logger());\napp.use(rateLimit(100, 60));\napp.use(jwt(SECRET));\napp.use(session({ secret: \"session-secret\" }));\napp.use(compress());\n\n// ── JSX SSR ───────────────────────────────────────────────\nconst Page = () =\u003e Html(Head(\"Home\"), Body(H1(\"Hello World\"), P(\"Welcome!\")));\napp.get(\"/\", () =\u003e render(Page()));\n\n// ── Cookies \u0026 IP ──────────────────────────────────────────\napp.post(\"/login\", (req) =\u003e\n  app.response()\n    .json({ ok: true, ip: req.ip })\n    .cookie(\"sessionId\", \"user_123\", {\n      httpOnly: true, secure: true, sameSite: \"Strict\", maxAge: 86400,\n    })\n);\napp.get(\"/profile\", (req) =\u003e ({\n  sessionId: req.cookies?.sessionId,\n  clientIp: req.ip,\n}));\n\n// ── Database ──────────────────────────────────────────────\nconst users = db.sqlite(\"./app.sqlite\", `\n  CREATE TABLE IF NOT EXISTS users (id TEXT PRIMARY KEY, name TEXT NOT NULL)\n`);\napp.get(\"/users\", () =\u003e users.query(\"SELECT * FROM users\"));\n\n// ── WebSockets ────────────────────────────────────────────\napp.ws(\"/chat\", {\n  open:    (ws) =\u003e ws.send(\"Welcome!\"),\n  message: (ws, msg) =\u003e ws.send(`Echo: ${msg}`),\n  close:   (ws) =\u003e console.log(\"disconnected\"),\n});\n\n// ── Auth \u0026 API keys ───────────────────────────────────────\napp.get(\"/protected\", auth(), (req) =\u003e ({ user: req.user }));\napp.get(\"/api\", apiKey({ keys: [\"key_123\"] }), () =\u003e ({ ok: true }));\napp.get(\"/admin\", every(auth(), async (req, next) =\u003e {\n  if (req.user?.role !== \"admin\")\n    return new Response(JSON.stringify({ error: \"Forbidden\" }), { status: 403 });\n  return next();\n}), () =\u003e ({ admin: true }));\n\n// ── Validated route group ─────────────────────────────────\napp.group(\"/items\", guard({ body: z.object({ name: z.string().min(1) }) }), (r) =\u003e {\n  r.post(\"/\", (req) =\u003e ({ created: req.parsedBody.name }));\n});\n\n// ── Helpers ───────────────────────────────────────────────\napp.get(\"/cached\",  cache(60)(() =\u003e ({ time: Date.now() })));\napp.post(\"/upload\", upload());\napp.get(\"/events\",  sse(), (req) =\u003e {\n  let i = 0;\n  const id = setInterval(() =\u003e {\n    req.sseSend({ count: i++ });\n    if (i \u003e= 10) clearInterval(id);\n  }, 1000);\n});\n\n// ── Validation ────────────────────────────────────────────\napp.post(\n  \"/items\",\n  validate(z.object({ name: z.string().min(1), price: z.number().positive() })),\n  (req) =\u003e ({ created: req.parsedBody })\n);\n\n// ── Cron ──────────────────────────────────────────────────\ncron(\"* * * * *\", () =\u003e console.log(\"💓 heartbeat\"));\n\n// ── OpenAPI + Scalar ──────────────────────────────────────\nconst api = app.openapi({ title: \"PrinceJS App\", version: \"1.0.0\" }, \"/docs\");\n\napi.route(\"GET\", \"/items\", {\n  summary: \"List items\",\n  tags: [\"items\"],\n  schema: {\n    query:    z.object({ q: z.string().optional() }),\n    response: z.array(z.object({ id: z.string(), name: z.string() })),\n  },\n}, () =\u003e [{ id: \"1\", name: \"Widget\" }]);\n\napi.route(\"POST\", \"/items\", {\n  summary: \"Create item\",\n  tags: [\"items\"],\n  schema: {\n    body:     z.object({ name: z.string().min(1), price: z.number().positive() }),\n    response: z.object({ id: z.string(), name: z.string() }),\n  },\n}, (req) =\u003e ({ id: crypto.randomUUID(), name: req.parsedBody.name }));\n\napp.listen(3000);\n```\n\n---\n\n## 📦 Installation\n\n```bash\nbun add princejs\n# or\nnpm install princejs\n```\n\n---\n\n## 🤝 Contributing\n\n```bash\ngit clone https://github.com/MatthewTheCoder1218/princejs\ncd princejs\nbun install\nbun test\n```\n\n---\n\n## 🔗 Links\n\n- 🌐 Website: [princejs.vercel.app](https://princejs.vercel.app)\n- 📦 npm: [npmjs.com/package/princejs](https://www.npmjs.com/package/princejs)\n- 💻 GitHub: [github.com/MatthewTheCoder1218/princejs](https://github.com/MatthewTheCoder1218/princejs)\n- 🐦 Twitter: [@princejs_bun](https://twitter.com/princejs_bun)\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**PrinceJS: ~5kB. Hono-speed. Everything included. 👑**\n\n*Built with ❤️ in Nigeria*\n\n\u003c/div\u003e","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthewthecoder1218%2Fprincejs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmatthewthecoder1218%2Fprincejs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatthewthecoder1218%2Fprincejs/lists"}