@coderbuzz/ken 0.1.0 → 0.1.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.
package/README.md CHANGED
@@ -4,38 +4,800 @@
4
4
 
5
5
  ### The Modern Standard for TypeScript Backends.
6
6
 
7
- Ken is a modern TypeScript backend framework designed to run seamlessly across
8
- Node.js, Deno, and Bun. It focuses on clarity, speed, and a clean developer
9
- experience without unnecessary abstraction.
7
+ Ken is a next-generation TypeScript backend framework designed to run seamlessly
8
+ across Node.js, Deno, and Bun. It focuses on clarity, speed, and a clean
9
+ developer experience without unnecessary abstraction.
10
10
 
11
- Ken is a minimalist, high-performance, and type-safe backend framework built
12
- entirely with TypeScript. Designed for the modern JavaScript ecosystem, Ken
13
- ensures your application runs consistently across Node.js, Deno, and Bun. Write
14
- once, deploy everywhere, and unleash the true potential of your server-side
15
- code.
11
+ Built entirely with TypeScript, Ken is minimalist, high-performance, and fully
12
+ type-safe. Designed for the modern JavaScript ecosystem, it ensures your
13
+ application runs consistently across Node.js, Deno, and Bun. Write once, deploy
14
+ everywhere, and unleash the true potential of your server-side code.
16
15
 
17
- Ken is your next-generation TypeScript backend framework. It was engineered from
18
- the ground up to deliver unparalleled developer experience (DX) and superior
19
- performance. By leveraging the power of modern TypeScript and a fluent, modular
20
- architecture, Ken allows developers to build scalable APIs and services with
21
- absolute type safety.
16
+ Engineered from the ground up to deliver unparalleled developer experience (DX)
17
+ and superior performance, Ken leverages the power of modern TypeScript and a
18
+ fluent, modular architecture to help developers build scalable APIs and services
19
+ with absolute type safety with full type inference, schema validation, and
20
+ built-in WebSocket support.
22
21
 
23
- ### Key Features:
22
+ <!-- Ken is a minimalist, high-performance, type-safe backend framework for
23
+ TypeScript. It runs natively on Node.js, Deno, and Bun — write once, deploy
24
+ anywhere — with full type inference, schema validation, and built-in WebSocket
25
+ support. -->
24
26
 
25
- - Runtime Agnostic: Seamlessly run your application on Node.js, Deno, and Bun
26
- without code changes.
27
+ ### Key Features
27
28
 
28
- - TypeScript Native: Full type-checking and autocompletion support out of the
29
- box.
29
+ - **Runtime Agnostic**: Seamlessly run your application on Node.js, Deno, and
30
+ Bun without code changes.
31
+ - **TypeScript Native**: Full type-checking and autocompletion support out of
32
+ the box.
33
+ - **Schema Validation**: Validate request params, query, headers, cookies, and
34
+ body with inline schemas.
35
+ - **Built-in Middleware**: JWT, CORS, sessions, compression, rate limiting,
36
+ secure headers, and more.
37
+ - **WebSocket Support**: Real-time connections with pub/sub, ping/pong, and
38
+ typed upgrade data.
39
+ - **Performance-Driven**: Minimal overhead engineered for high throughput and
40
+ low latency.
41
+ - **Modular & Extensible**: Easily integrate with existing libraries and scale
42
+ complexity as needed.
30
43
 
31
- - Performance-Driven: Minimal overhead designed for maximal throughput and low
32
- latency.
44
+ ---
33
45
 
34
- - Modular & Extensible: Easily integrate with existing libraries and scale
35
- complexity as needed.
46
+ ## Installation
47
+
48
+ ```sh
49
+ # Bun
50
+ bun add @coderbuzz/ken
51
+
52
+ # npm
53
+ npm install @coderbuzz/ken
54
+
55
+ # Deno
56
+ import { AppServer } from "npm:@coderbuzz/ken";
57
+ ```
58
+
59
+ For Node.js, also install a peer for ESM compatibility:
60
+
61
+ ```sh
62
+ npm install tsx
63
+ node --import tsx/esm server.ts
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Quick Start
69
+
70
+ ```ts
71
+ import { AppServer } from "@coderbuzz/ken";
72
+
73
+ const app = new AppServer({ port: 3000 });
74
+
75
+ app.get("/", "Hello, Ken!");
76
+
77
+ const { hostname, port } = await app.run();
78
+ console.log(`Listening on ${hostname}:${port}`);
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Routing
84
+
85
+ ### Static Routes
86
+
87
+ ```ts
88
+ app.get("/", "Hello Ken!");
89
+ app.get("/health", "OK");
90
+ app.get("/version", { version: "1.0.0" }); // plain objects are JSON-serialized
91
+ ```
92
+
93
+ ### Dynamic Params
94
+
95
+ ```ts
96
+ app.get("/users/:id", (ctx) => new Response(`User ${ctx.params.id}`));
97
+
98
+ app.get(
99
+ "/posts/:postId/comments/:commentId",
100
+ (ctx) =>
101
+ new Response(`Post ${ctx.params.postId}, Comment ${ctx.params.commentId}`),
102
+ );
103
+ ```
104
+
105
+ ### Optional Params & Wildcards
106
+
107
+ ```ts
108
+ app.get(
109
+ "/optional/:id?",
110
+ (ctx) => new Response(`ID: ${ctx.params.id ?? "none"}`),
111
+ );
112
+
113
+ app.get("/files/*", (ctx) => new Response(`File: ${ctx.params["*"]}`));
114
+ ```
115
+
116
+ ### HTTP Methods
117
+
118
+ ```ts
119
+ app.post("/items", handler);
120
+ app.put("/items/:id", handler);
121
+ app.patch("/items/:id", handler);
122
+ app.delete("/items/:id", handler);
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Schema Validation
128
+
129
+ Ken validates request data inline via the schema object (first argument before
130
+ the handler). Use
131
+ [`@coderbuzz/kyo`](https://www.npmjs.com/package/@coderbuzz/kyo) for schema
132
+ builders.
133
+
134
+ ```ts
135
+ import {
136
+ boolean,
137
+ coerce,
138
+ number,
139
+ object,
140
+ optional,
141
+ string,
142
+ } from "@coderbuzz/kyo";
143
+ ```
144
+
145
+ ### Params
146
+
147
+ ```ts
148
+ app.get("/products/:id", {
149
+ params: { id: coerce(number()) },
150
+ }, (ctx) => Response.json({ productId: ctx.params.id }));
151
+ // ctx.params.id is typed as number
152
+ ```
153
+
154
+ ### Query
155
+
156
+ ```ts
157
+ app.get("/search", {
158
+ query: {
159
+ q: string({ min: 1 }),
160
+ page: coerce(number({ min: 1, max: 100 })),
161
+ limit: optional(coerce(number({ min: 10, max: 100 }))),
162
+ },
163
+ }, (ctx) => Response.json({ search: ctx.query.q, page: ctx.query.page }));
164
+ ```
165
+
166
+ ### Headers
167
+
168
+ ```ts
169
+ app.get("/api/resource", {
170
+ headers: { "x-api-key": string({ min: 10 }) },
171
+ }, (ctx) => Response.json({ key: ctx.headers["x-api-key"] }));
172
+ ```
173
+
174
+ ### Cookies
175
+
176
+ ```ts
177
+ app.get(
178
+ "/api/profile",
179
+ {
180
+ cookies: {
181
+ sessionId: string({ min: 5 }),
182
+ premium: optional(coerce(boolean())),
183
+ },
184
+ },
185
+ (ctx) =>
186
+ Response.json({
187
+ session: ctx.cookies.sessionId,
188
+ isPremium: ctx.cookies.premium,
189
+ }),
190
+ );
191
+ ```
192
+
193
+ ### JSON Body
194
+
195
+ ```ts
196
+ app.post("/api/users", {
197
+ json: object({
198
+ name: string({ min: 2 }),
199
+ age: number({ min: 18 }),
200
+ active: boolean(),
201
+ email: optional(string()),
202
+ }),
203
+ }, async (ctx) => {
204
+ const body = await ctx.json;
205
+ return Response.json({ name: body.name, age: body.age });
206
+ });
207
+ ```
208
+
209
+ ### Text Body
210
+
211
+ ```ts
212
+ app.post("/api/echo", {
213
+ text: string({ min: 5 }),
214
+ }, async (ctx) => new Response(await ctx.text));
215
+ ```
216
+
217
+ ### Form Body
218
+
219
+ ```ts
220
+ app.post("/api/submit", {
221
+ form: { field: string({ min: 3 }) },
222
+ }, async (ctx) => {
223
+ const data = await ctx.form;
224
+ return new Response(data.field);
225
+ });
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Middleware & State
231
+
232
+ Middleware runs before the handler and returns typed state accessible via
233
+ `ctx.state`.
234
+
235
+ ### Per-Route State
236
+
237
+ ```ts
238
+ app.get("/protected", {
239
+ state: {
240
+ auth: (ctx) => {
241
+ const token = ctx.headers["authorization"];
242
+ if (token !== "Bearer valid-token") {
243
+ throw new Response("Unauthorized", { status: 401 });
244
+ }
245
+ return { userId: "user123", role: "admin" };
246
+ },
247
+ },
248
+ }, (ctx) => Response.json({ user: ctx.state.auth.userId }));
249
+ ```
250
+
251
+ ### Async State
252
+
253
+ ```ts
254
+ app.get("/data", {
255
+ state: {
256
+ data: async () => {
257
+ const result = await fetchFromDb();
258
+ return result;
259
+ },
260
+ },
261
+ }, (ctx) => Response.json(ctx.state.data));
262
+ ```
263
+
264
+ ### Multiple Guards
265
+
266
+ ```ts
267
+ app.post("/admin/action", {
268
+ state: {
269
+ auth: (ctx) => {
270
+ const token = ctx.headers["authorization"];
271
+ if (!token) throw new Response("Unauthorized", { status: 401 });
272
+ return { userId: "admin1", role: "admin" };
273
+ },
274
+ permission: (ctx) => {
275
+ if ((ctx.state as any).auth.role !== "admin") {
276
+ throw new Response("Forbidden", { status: 403 });
277
+ }
278
+ },
279
+ },
280
+ }, () => Response.json({ message: "Action performed" }));
281
+ ```
282
+
283
+ ### `onFinish` Callback
284
+
285
+ ```ts
286
+ app.get("/with-logging", {
287
+ state: {
288
+ logger: (ctx) => {
289
+ const start = Date.now();
290
+ ctx.onFinish((resp) => {
291
+ console.log(
292
+ `${ctx.method} ${ctx.url} - ${resp?.status} - ${
293
+ Date.now() - start
294
+ }ms`,
295
+ );
296
+ });
297
+ },
298
+ },
299
+ }, () => new Response("logged"));
300
+ ```
301
+
302
+ ### `define()` — Scoped Middleware
303
+
304
+ Apply middleware to a group of routes with full type inference:
305
+
306
+ ```ts
307
+ app.define(
308
+ {
309
+ userId: (ctx) => ctx.headers["x-user-id"] || "guest",
310
+ isAdmin: (ctx) => ctx.headers["x-role"] === "admin",
311
+ },
312
+ (app) => {
313
+ app.get(
314
+ "/me",
315
+ (ctx) =>
316
+ Response.json({ userId: ctx.state.userId, isAdmin: ctx.state.isAdmin }),
317
+ );
318
+ app.get("/dashboard", (ctx) => {
319
+ if (!ctx.state.isAdmin) throw new Response("Forbidden", { status: 403 });
320
+ return Response.json({ admin: true });
321
+ });
322
+ },
323
+ );
324
+ ```
325
+
326
+ ### `apply()` — Global Middleware
327
+
328
+ ```ts
329
+ // Side-effect middleware (logging, metrics)
330
+ app.apply("/*", (ctx) => {
331
+ console.log(ctx.method, ctx.url);
332
+ });
333
+
334
+ // State-producing middleware
335
+ app.apply("/*", { auth: (ctx) => verifyAuth(ctx) });
336
+ ```
337
+
338
+ ### `use()` — Mount Sub-Apps
339
+
340
+ ```ts
341
+ const api = new App();
342
+ api.get("/users", handler);
343
+ api.get("/posts", handler);
344
+
345
+ app.use("/api/v1", api);
346
+ ```
347
+
348
+ ---
349
+
350
+ ## Cookies
351
+
352
+ ### Reading Cookies
353
+
354
+ ```ts
355
+ app.get("/profile", (ctx) => {
356
+ return Response.json({ theme: ctx.cookies.theme });
357
+ });
358
+ ```
359
+
360
+ ### Setting Cookies
361
+
362
+ ```ts
363
+ app.get("/login", (ctx) => {
364
+ ctx.setCookie("session", "abc123", {
365
+ path: "/",
366
+ httpOnly: true,
367
+ secure: true,
368
+ sameSite: "Strict",
369
+ maxAge: 3600,
370
+ });
371
+ return new Response("Logged in");
372
+ });
373
+ ```
374
+
375
+ ---
376
+
377
+ ## Error Handling
378
+
379
+ ```ts
380
+ // Custom app-level error handler
381
+ app.onError((error, ctx) => {
382
+ console.error(ctx.method, ctx.url, error);
383
+ return Response.json(
384
+ {
385
+ message: error instanceof Error ? error.message : "Internal Server Error",
386
+ },
387
+ { status: 500 },
388
+ );
389
+ });
390
+
391
+ // Custom 404 handler
392
+ app.notFound((ctx) => {
393
+ return Response.json({ error: "Not Found", path: ctx.url }, { status: 404 });
394
+ });
395
+
396
+ // Throw a Response to short-circuit with a specific status
397
+ app.get("/secret", (ctx) => {
398
+ throw new Response("Forbidden", { status: 403 });
399
+ });
400
+ ```
401
+
402
+ ---
403
+
404
+ ## Built-in Middleware
405
+
406
+ ### CORS
407
+
408
+ ```ts
409
+ import { cors } from "@coderbuzz/ken";
410
+
411
+ const api = cors({ origin: "https://example.com", credentials: true });
412
+ api.get("/", () => Response.json({ data: true }));
413
+ app.use("/api", api);
414
+ ```
415
+
416
+ ### JWT
417
+
418
+ ```ts
419
+ import { jwt, signJwt } from "@coderbuzz/ken";
420
+
421
+ // Sign a token
422
+ app.get("/token", async () => {
423
+ const token = await signJwt({
424
+ sub: "user123",
425
+ exp: Math.floor(Date.now() / 1000) + 3600,
426
+ }, "secret");
427
+ return Response.json({ token });
428
+ });
429
+
430
+ // Protect a route
431
+ app.get("/secure", {
432
+ state: { auth: jwt({ secret: "secret" }) },
433
+ }, (ctx) => Response.json({ payload: ctx.state.auth }));
434
+ ```
435
+
436
+ ### Session
437
+
438
+ ```ts
439
+ import { session } from "@coderbuzz/ken";
440
+
441
+ const userSession = session({
442
+ cookieName: "_sid",
443
+ validate: (cookieValue) => {
444
+ const user = db.getUser(cookieValue);
445
+ if (!user?.active) throw new Response("Unauthorized", { status: 401 });
446
+ return user;
447
+ },
448
+ });
449
+
450
+ app.get("/dashboard", {
451
+ state: { session: userSession },
452
+ }, (ctx) => Response.json({ user: ctx.state.session }));
453
+ ```
454
+
455
+ ### Basic Auth
456
+
457
+ ```ts
458
+ import { basicAuth } from "@coderbuzz/ken";
459
+
460
+ app.get("/admin", {
461
+ state: { auth: basicAuth({ username: "admin", password: "secret" }) },
462
+ }, (ctx) => Response.json({ user: ctx.state.auth.username }));
463
+ ```
464
+
465
+ ### Bearer Auth
466
+
467
+ ```ts
468
+ import { bearerAuth } from "@coderbuzz/ken";
469
+
470
+ app.get("/api/resource", {
471
+ state: { auth: bearerAuth({ token: "my-token" }) },
472
+ }, (ctx) => Response.json({ token: ctx.state.auth.token }));
473
+ ```
474
+
475
+ ### Logger
476
+
477
+ ```ts
478
+ import { logger } from "@coderbuzz/ken";
479
+
480
+ app.use(logger());
481
+ ```
482
+
483
+ ### Request ID
484
+
485
+ ```ts
486
+ import { requestId } from "@coderbuzz/ken";
487
+
488
+ app.get("/request", {
489
+ state: { reqId: requestId() },
490
+ }, (ctx) => Response.json({ id: ctx.state.reqId }));
491
+ ```
492
+
493
+ ### Secure Headers
494
+
495
+ ```ts
496
+ import { secureHeaders } from "@coderbuzz/ken";
497
+
498
+ app.get("/page", {
499
+ state: { sec: secureHeaders() },
500
+ }, () => new Response("secure"));
501
+ ```
502
+
503
+ ### Compression
504
+
505
+ ```ts
506
+ import { compress } from "@coderbuzz/ken";
507
+
508
+ app.get("/data", {
509
+ state: { encoding: compress() },
510
+ }, (ctx) => Response.json({ encoding: ctx.state.encoding.encoding }));
511
+ ```
512
+
513
+ ### Cache
514
+
515
+ ```ts
516
+ import { cache } from "@coderbuzz/ken";
517
+
518
+ app.get("/static", {
519
+ state: { caching: cache({ maxAge: 3600, public: true }) },
520
+ }, () => new Response("cached content"));
521
+ ```
522
+
523
+ ### Rate Limiting / Body Limit
524
+
525
+ ```ts
526
+ import { bodyLimit } from "@coderbuzz/ken";
527
+
528
+ app.post("/upload", {
529
+ state: { limit: bodyLimit({ maxSize: 1_000_000 }) }, // 1 MB
530
+ }, async (ctx) => {
531
+ const body = await ctx.json;
532
+ return Response.json({ received: body });
533
+ });
534
+ ```
535
+
536
+ ### Timeout
537
+
538
+ ```ts
539
+ import { timeout } from "@coderbuzz/ken";
540
+
541
+ app.get("/slow", {
542
+ state: { timeoutSig: timeout({ duration: 5000 }) },
543
+ }, () => new Response("fast enough"));
544
+ ```
545
+
546
+ ### Timing
547
+
548
+ ```ts
549
+ import { timing } from "@coderbuzz/ken";
550
+
551
+ app.get("/timed", {
552
+ state: { perf: timing() },
553
+ }, () => new Response("timed"));
554
+ ```
555
+
556
+ ### IP Restriction
557
+
558
+ ```ts
559
+ import { ipRestriction } from "@coderbuzz/ken";
560
+
561
+ app.get("/internal", {
562
+ state: { ipCheck: ipRestriction({ allowList: ["127.0.0.1", "::1"] }) },
563
+ }, () => new Response("allowed"));
564
+ ```
565
+
566
+ ### CSRF Protection
567
+
568
+ ```ts
569
+ import { csrf } from "@coderbuzz/ken";
570
+
571
+ app.post("/form", {
572
+ state: { protection: csrf({ origin: ["https://example.com"] }) },
573
+ }, () => Response.json({ success: true }));
574
+ ```
575
+
576
+ ### ETag
577
+
578
+ ```ts
579
+ import { etag } from "@coderbuzz/ken";
580
+
581
+ app.get("/resource", {
582
+ state: { etagValue: etag() },
583
+ }, (ctx) => {
584
+ const tag = '"v1"';
585
+ if (ctx.state.etagValue === tag) return new Response(null, { status: 304 });
586
+ return new Response("content", { headers: { ETag: tag } });
587
+ });
588
+ ```
589
+
590
+ ### Combining Middleware
591
+
592
+ ```ts
593
+ app.get("/combined", {
594
+ state: {
595
+ reqId: requestId(),
596
+ perf: timing(),
597
+ sec: secureHeaders(),
598
+ },
599
+ }, (ctx) => Response.json({ id: ctx.state.reqId }));
600
+ ```
601
+
602
+ ---
603
+
604
+ ## WebSocket
605
+
606
+ ### Basic Echo
607
+
608
+ ```ts
609
+ app.ws("/echo", {
610
+ message(peer, message) {
611
+ peer.send(message);
612
+ },
613
+ });
614
+ ```
615
+
616
+ ### Lifecycle Hooks
617
+
618
+ ```ts
619
+ app.ws("/chat", {
620
+ open(peer) {
621
+ peer.send("connected");
622
+ },
623
+ message(peer, message) {
624
+ peer.send(`echo: ${message}`);
625
+ },
626
+ close(peer, code, reason) {
627
+ console.log("disconnected", code, reason);
628
+ },
629
+ });
630
+ ```
631
+
632
+ ### Typed Upgrade Data
633
+
634
+ ```ts
635
+ app.ws<{ userId: string }>("/auth", {
636
+ upgrade(req) {
637
+ const url = new URL(req.url);
638
+ const userId = url.searchParams.get("userId");
639
+ if (!userId) return new Response("Unauthorized", { status: 401 });
640
+ return { userId };
641
+ },
642
+ open(peer) {
643
+ peer.send(`Hello ${peer.data.userId}`);
644
+ },
645
+ message(peer, message) {
646
+ peer.send(`${peer.data.userId}: ${message}`);
647
+ },
648
+ });
649
+ ```
650
+
651
+ ### Native Pub/Sub
652
+
653
+ ```ts
654
+ app.ws("/chat", {
655
+ open(peer) {
656
+ peer.subscribe("chat");
657
+ peer.publish("chat", "someone joined");
658
+ },
659
+ message(peer, message) {
660
+ peer.publish("chat", message);
661
+ peer.send(`you: ${message}`);
662
+ },
663
+ close(peer) {
664
+ peer.publish("chat", "someone left");
665
+ },
666
+ });
667
+ ```
668
+
669
+ ### WsTopicHub (App-Level Pub/Sub)
670
+
671
+ ```ts
672
+ import { WsTopicHub } from "@coderbuzz/ken";
673
+
674
+ const hub = new WsTopicHub();
675
+
676
+ app.ws("/notifications", {
677
+ open(peer) {
678
+ hub.subscribe(peer, "alerts");
679
+ },
680
+ close(peer) {
681
+ hub.unsubscribeAll(peer);
682
+ },
683
+ message(peer, _msg) {
684
+ hub.markAlive(peer);
685
+ },
686
+ });
687
+
688
+ // Publish from anywhere (e.g., another route)
689
+ app.post("/broadcast", async (ctx) => {
690
+ const { message } = await ctx.json;
691
+ hub.publish("alerts", message);
692
+ return Response.json({ sent: true });
693
+ });
694
+ ```
695
+
696
+ ### Ping/Pong (Heartbeat)
697
+
698
+ ```ts
699
+ app.ws("/live", {
700
+ pong(peer) {
701
+ console.log(`${peer.remoteAddress} is alive`);
702
+ },
703
+ message(peer, msg) {
704
+ peer.send(msg);
705
+ },
706
+ }, { pingInterval: 30, pongTimeout: 10 });
707
+ ```
708
+
709
+ ---
710
+
711
+ ## Streaming Response
712
+
713
+ ```ts
714
+ app.get("/stream", () => {
715
+ const stream = new ReadableStream({
716
+ start(controller) {
717
+ controller.enqueue(new TextEncoder().encode("chunk 1"));
718
+ controller.enqueue(new TextEncoder().encode("chunk 2"));
719
+ controller.close();
720
+ },
721
+ });
722
+ return new Response(stream);
723
+ });
724
+ ```
725
+
726
+ ---
727
+
728
+ ## File Utilities
729
+
730
+ ```ts
731
+ import {
732
+ listDirectory,
733
+ receiveFiles,
734
+ saveFile,
735
+ sendFile,
736
+ } from "@coderbuzz/ken";
737
+
738
+ // Serve a file
739
+ app.get("/download", (ctx) => sendFile(ctx, "./assets/file.pdf"));
740
+
741
+ // List directory
742
+ app.get("/files", (ctx) => listDirectory(ctx, "./uploads"));
743
+
744
+ // Receive uploaded files
745
+ app.post("/upload", async (ctx) => {
746
+ const files = await receiveFiles(ctx);
747
+ for (const file of files) {
748
+ await saveFile(file, "./uploads");
749
+ }
750
+ return Response.json({ count: files.length });
751
+ });
752
+ ```
753
+
754
+ ---
755
+
756
+ ## Utilities
757
+
758
+ ```ts
759
+ import {
760
+ compressString,
761
+ decompressString,
762
+ decryptString,
763
+ encryptString,
764
+ generateSecretKey,
765
+ getPathname,
766
+ memoize,
767
+ } from "@coderbuzz/ken";
768
+
769
+ // Encryption
770
+ const key = await generateSecretKey();
771
+ const encrypted = await encryptString("hello", key);
772
+ const decrypted = await decryptString(encrypted, key);
773
+
774
+ // Compression
775
+ const compressed = await compressString("large text...");
776
+ const original = await decompressString(compressed);
777
+
778
+ // Memoization
779
+ const cached = memoize(expensiveFn, { ttl: 60_000 });
780
+ ```
781
+
782
+ ---
783
+
784
+ ## Runtime Detection
785
+
786
+ ```ts
787
+ import { isBun, isDeno, isNode } from "@coderbuzz/ken";
788
+
789
+ if (isBun) console.log("Running on Bun");
790
+ if (isDeno) console.log("Running on Deno");
791
+ if (isNode) console.log("Running on Node.js");
792
+ ```
793
+
794
+ ---
36
795
 
37
- ## Build
796
+ ## Remote Info
38
797
 
39
- ```bash
40
- bun run build
798
+ ```ts
799
+ app.get("/ip", (ctx) => {
800
+ const { address, port } = ctx.remoteInfo;
801
+ return Response.json({ address, port });
802
+ });
41
803
  ```