princejs 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/Readme.md +86 -78
  2. package/package.json +1 -1
package/Readme.md CHANGED
@@ -28,7 +28,7 @@ Benchmarked with `oha -c 100 -z 30s` on Windows 10:
28
28
  | Fastify | 15,519 | 16,434 |
29
29
  | Express | 13,138 | 13,458 |
30
30
 
31
- > PrinceJS is **2.3× faster than Express**, matches Hono head-to-head, and sits at just **6.3kB gzipped** — loads in ~122ms on a slow 3G connection.
31
+ > 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.
32
32
 
33
33
  ---
34
34
 
@@ -61,11 +61,9 @@ app.listen(3000);
61
61
 
62
62
  | Feature | Import |
63
63
  |---------|--------|
64
- | Routing, WebSockets, OpenAPI, Plugins, Lifecycle Hooks, Cookies, IP | `princejs` |
65
- | **Route Grouping** | `princejs` |
66
- | CORS, Logger, JWT, Auth, Rate Limit, Validate, Compress, Session, API Key | `princejs/middleware` |
67
- | **Secure Headers, Timeout, Request ID, IP Restriction, Static Files, JWKS** | `princejs/middleware` |
68
- | File Uploads, SSE, In-memory Cache, **Streaming** | `princejs/helpers` |
64
+ | Routing, Route Grouping, WebSockets, OpenAPI, Plugins, Lifecycle Hooks, Cookies, IP | `princejs` |
65
+ | CORS, Logger, JWT, JWKS, Auth, Rate Limit, Validate, Compress, Session, API Key, Secure Headers, Timeout, Request ID, IP Restriction, Static Files | `princejs/middleware` |
66
+ | File Uploads, SSE, Streaming, In-memory Cache | `princejs/helpers` |
69
67
  | Cron Scheduler | `princejs/scheduler` |
70
68
  | JSX / SSR | `princejs/jsx` |
71
69
  | SQLite Database | `princejs/db` |
@@ -157,48 +155,56 @@ app.post("/admin", (req) => {
157
155
 
158
156
  ---
159
157
 
160
- ---
161
158
 
162
- ## 📁 Route Grouping
159
+ ## 🗂️ Route Grouping
163
160
 
164
- Namespace routes under a shared prefix with optional shared middleware. Zero overhead at request time — just registers prefixed routes in the trie.
161
+ Group routes under a shared prefix with optional shared middleware. Zero overhead at request time — purely a registration convenience.
165
162
 
166
163
  ```ts
164
+ import { prince } from "princejs";
165
+
166
+ const app = prince();
167
+
167
168
  // Basic grouping
168
169
  app.group("/api", (r) => {
169
- r.get("/users", () => ({ users: [] })); // → GET /api/users
170
- r.post("/users", (req) => req.parsedBody); // → POST /api/users
171
- r.get("/users/:id", (req) => ({ id: req.params?.id })); // → GET /api/users/:id
170
+ r.get("/users", () => ({ users: [] }));
171
+ r.post("/users", (req) => ({ created: req.parsedBody }));
172
+ r.get("/users/:id", (req) => ({ id: req.params?.id }));
172
173
  });
174
+ // → GET /api/users
175
+ // → POST /api/users
176
+ // → GET /api/users/:id
173
177
 
174
178
  // With shared middleware — applies to every route in the group
175
179
  import { auth } from "princejs/middleware";
176
180
 
177
181
  app.group("/admin", auth(), (r) => {
178
- r.get("/stats", () => ({ ok: true })); // → GET /admin/stats
179
- r.delete("/user", () => ({ deleted: true })); // → DELETE /admin/user
182
+ r.get("/stats", () => ({ stats: {} }));
183
+ r.delete("/users/:id", (req) => ({ deleted: req.params?.id }));
180
184
  });
181
185
 
182
186
  // Chainable
183
187
  app
184
188
  .group("/v1", (r) => { r.get("/ping", () => ({ v: 1 })); })
185
189
  .group("/v2", (r) => { r.get("/ping", () => ({ v: 2 })); });
190
+
191
+ app.listen(3000);
186
192
  ```
187
193
 
188
194
  ---
189
195
 
190
- ## 🔐 Security Middleware
196
+ ## 🛡️ Secure Headers
191
197
 
192
- ### Secure Headers
193
-
194
- One call sets X-Frame-Options, HSTS, X-Content-Type-Options, X-XSS-Protection, and Referrer-Policy:
198
+ One call sets all the security headers your production app needs:
195
199
 
196
200
  ```ts
197
201
  import { secureHeaders } from "princejs/middleware";
198
202
 
199
203
  app.use(secureHeaders());
204
+ // Sets: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection,
205
+ // Strict-Transport-Security, Referrer-Policy
200
206
 
201
- // Custom overrides
207
+ // Custom options
202
208
  app.use(secureHeaders({
203
209
  xFrameOptions: "DENY",
204
210
  contentSecurityPolicy: "default-src 'self'",
@@ -207,18 +213,25 @@ app.use(secureHeaders({
207
213
  }));
208
214
  ```
209
215
 
210
- ### Request Timeout
216
+ ---
217
+
218
+ ## ⏱️ Request Timeout
211
219
 
212
220
  Kill hanging requests before they pile up:
213
221
 
214
222
  ```ts
215
223
  import { timeout } from "princejs/middleware";
216
224
 
217
- app.use(timeout(5000)); // 408 after 5s
218
- app.use(timeout(3000, "Gateway Timeout")); // custom message
225
+ app.use(timeout(5000)); // 5 second global timeout → 408
226
+ app.use(timeout(3000, "Slow!")); // custom message
227
+
228
+ // Per-route timeout
229
+ app.get("/heavy", timeout(10000), (req) => heavyOperation());
219
230
  ```
220
231
 
221
- ### Request ID
232
+ ---
233
+
234
+ ## 🏷️ Request ID
222
235
 
223
236
  Attach a unique ID to every request for distributed tracing and log correlation:
224
237
 
@@ -228,18 +241,20 @@ import { requestId } from "princejs/middleware";
228
241
  app.use(requestId());
229
242
  // → sets req.id and X-Request-ID response header
230
243
 
231
- // Custom header or generator
232
- app.use(requestId({
233
- header: "X-Trace-ID",
234
- generator: () => `req-${Date.now()}`,
235
- }));
244
+ // Custom header name
245
+ app.use(requestId({ header: "X-Trace-ID" }));
246
+
247
+ // Custom generator
248
+ app.use(requestId({ generator: () => `req-${Date.now()}` }));
236
249
 
237
250
  app.get("/", (req) => ({ requestId: req.id }));
238
251
  ```
239
252
 
240
- ### IP Restriction
253
+ ---
254
+
255
+ ## 🚫 IP Restriction
241
256
 
242
- Allow or deny specific IPs:
257
+ Allow or block specific IPs:
243
258
 
244
259
  ```ts
245
260
  import { ipRestriction } from "princejs/middleware";
@@ -247,30 +262,13 @@ import { ipRestriction } from "princejs/middleware";
247
262
  // Only allow these IPs
248
263
  app.use(ipRestriction({ allowList: ["192.168.1.1", "10.0.0.1"] }));
249
264
 
250
- // Block specific IPs
265
+ // Block these IPs
251
266
  app.use(ipRestriction({ denyList: ["1.2.3.4"] }));
252
267
  ```
253
268
 
254
- ### JWKS — Auth0, Clerk, Supabase
255
-
256
- Verify JWTs against a remote JWKS endpoint — no symmetric key needed:
257
-
258
- ```ts
259
- import { jwks } from "princejs/middleware";
260
-
261
- // Auth0
262
- app.use(jwks("https://YOUR_DOMAIN.auth0.com/.well-known/jwks.json"));
263
-
264
- // Clerk
265
- app.use(jwks("https://YOUR_CLERK_DOMAIN/.well-known/jwks.json"));
266
-
267
- // Keys are cached automatically — only fetched on rotation
268
- app.get("/protected", auth(), (req) => ({ user: req.user }));
269
- ```
270
-
271
269
  ---
272
270
 
273
- ## 📂 Static Files
271
+ ## 📁 Static Files
274
272
 
275
273
  Serve a directory of static files. Falls through to your routes if the file doesn't exist:
276
274
 
@@ -278,46 +276,67 @@ Serve a directory of static files. Falls through to your routes if the file does
278
276
  import { serveStatic } from "princejs/middleware";
279
277
 
280
278
  app.use(serveStatic("./public"));
281
-
282
- // Your API routes still work normally
283
- app.get("/api/users", () => ({ users: [] }));
279
+ // → GET /logo.png serves ./public/logo.png
280
+ // GET / serves ./public/index.html
281
+ // → GET /api/users falls through to your route handler
284
282
  ```
285
283
 
286
284
  ---
287
285
 
288
286
  ## 🌊 Streaming
289
287
 
290
- Stream responses chunk by chunk perfect for AI/LLM token output:
288
+ Stream chunked responses for AI/LLM output, large payloads, or anything that generates data over time:
291
289
 
292
290
  ```ts
293
291
  import { stream } from "princejs/helpers";
294
292
 
295
- // Async generator (cleanest for AI output)
293
+ // Async generator cleanest for AI token streaming
296
294
  app.get("/ai", stream(async function*(req) {
297
295
  yield "Hello ";
296
+ await delay(100);
298
297
  yield "from ";
299
298
  yield "PrinceJS!";
300
299
  }));
301
300
 
302
- // Callback style
303
- app.get("/stream", stream((req) => {
301
+ // Async callback
302
+ app.get("/data", stream(async (req) => {
304
303
  req.streamSend("chunk 1");
304
+ await fetchMoreData();
305
305
  req.streamSend("chunk 2");
306
306
  }));
307
307
 
308
- // Async callback
309
- app.get("/slow-stream", stream(async (req) => {
310
- req.streamSend("Starting...");
311
- await fetch("https://api.openai.com/v1/chat/completions", { /* ... */ })
312
- .then(res => res.body?.pipeTo(new WritableStream({
313
- write(chunk) { req.streamSend(chunk); }
314
- })));
315
- }));
308
+ // Custom content type for binary or JSON streams
309
+ app.get("/events", stream(async function*(req) {
310
+ for (const item of items) {
311
+ yield JSON.stringify(item) + "\n";
312
+ }
313
+ }, { contentType: "application/x-ndjson" }));
314
+ ```
315
+
316
+ ---
317
+
318
+ ## 🔑 JWKS / Third-Party Auth
319
+
320
+ Verify JWTs from Auth0, Clerk, Supabase, or any JWKS endpoint — no symmetric key needed:
321
+
322
+ ```ts
323
+ import { jwks } from "princejs/middleware";
316
324
 
317
- // Custom content type
318
- app.get("/binary", stream(() => { /* ... */ }, { contentType: "application/octet-stream" }));
325
+ // Auth0
326
+ app.use(jwks("https://your-domain.auth0.com/.well-known/jwks.json"));
327
+
328
+ // Clerk
329
+ app.use(jwks("https://your-clerk-domain.clerk.accounts.dev/.well-known/jwks.json"));
330
+
331
+ // Supabase
332
+ app.use(jwks("https://your-project.supabase.co/auth/v1/.well-known/jwks.json"));
333
+
334
+ // req.user is set after verification, same as jwt()
335
+ app.get("/protected", auth(), (req) => ({ user: req.user }));
319
336
  ```
320
337
 
338
+ ---
339
+
321
340
  ## 📖 OpenAPI + Scalar Docs ✨
322
341
 
323
342
  Auto-generate an OpenAPI 3.0 spec and serve a beautiful [Scalar](https://scalar.com) UI — all from a single `app.openapi()` call.
@@ -600,17 +619,6 @@ app.post(
600
619
  (req) => ({ created: req.parsedBody })
601
620
  );
602
621
 
603
- // ── Route groups ─────────────────────────────────────────
604
- app.group("/v1", (r) => {
605
- r.get("/status", () => ({ version: 1, ok: true }));
606
- });
607
-
608
- // ── Streaming ─────────────────────────────────────────────
609
- app.get("/ai", stream(async function*() {
610
- yield "Hello ";
611
- yield "World!";
612
- }));
613
-
614
622
  // ── Cron ──────────────────────────────────────────────────
615
623
  cron("* * * * *", () => console.log("💓 heartbeat"));
616
624
 
@@ -672,7 +680,7 @@ bun test
672
680
 
673
681
  <div align="center">
674
682
 
675
- **PrinceJS: 6.3kB. Hono-speed. Everything included. 👑**
683
+ **PrinceJS: ~5kB. Hono-speed. Everything included. 👑**
676
684
 
677
685
  *Built with ❤️ in Nigeria*
678
686
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "princejs",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "An easy and fast backend framework that is among the top three — by a 13yo developer, for developers.",
5
5
  "main": "dist/prince.js",
6
6
  "types": "dist/prince.d.ts",