@coderbuzz/ken 0.1.1 → 0.1.3

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