shokupan 0.5.0 → 0.6.1
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 +11 -8
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/context.d.ts +90 -7
- package/dist/index.cjs +746 -453
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +690 -419
- package/dist/index.js.map +1 -1
- package/dist/json-parser-B3dnQmCC.js +35 -0
- package/dist/json-parser-B3dnQmCC.js.map +1 -0
- package/dist/json-parser-COdZ0fqY.cjs +35 -0
- package/dist/json-parser-COdZ0fqY.cjs.map +1 -0
- package/dist/{openapi-analyzer-z-7AoFRC.cjs → openapi-analyzer-Bei1sVWp.cjs} +33 -16
- package/dist/openapi-analyzer-Bei1sVWp.cjs.map +1 -0
- package/dist/{openapi-analyzer-D7y6Qa38.js → openapi-analyzer-Ce_7JxZh.js} +33 -16
- package/dist/openapi-analyzer-Ce_7JxZh.js.map +1 -0
- package/dist/plugins/proxy.d.ts +2 -0
- package/dist/plugins/rate-limit.d.ts +1 -0
- package/dist/plugins/scalar.d.ts +1 -1
- package/dist/router.d.ts +125 -55
- package/dist/{server-adapter-BWrEJbKL.js → server-adapter-0xH174zz.js} +4 -2
- package/dist/server-adapter-0xH174zz.js.map +1 -0
- package/dist/{server-adapter-fVKP60e0.cjs → server-adapter-DFhwlK8e.cjs} +4 -2
- package/dist/server-adapter-DFhwlK8e.cjs.map +1 -0
- package/dist/shokupan.d.ts +66 -7
- package/dist/types.d.ts +63 -3
- package/dist/util/datastore.d.ts +6 -0
- package/dist/util/json-parser.d.ts +12 -0
- package/dist/util/plugin-deps.d.ts +25 -0
- package/package.json +73 -13
- package/dist/buntest.d.ts +0 -1
- package/dist/openapi-analyzer-D7y6Qa38.js.map +0 -1
- package/dist/openapi-analyzer-z-7AoFRC.cjs.map +0 -1
- package/dist/server-adapter-BWrEJbKL.js.map +0 -1
- package/dist/server-adapter-fVKP60e0.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@ Shokupan is designed to make building APIs delightful again. With zero-config de
|
|
|
7
7
|
|
|
8
8
|
### Note: Shokupan is still in alpha and is not guaranteed to be stable. Please use with caution. We will be adding more features and APIs in the future. Please file an issue if you find any bugs or have suggestions for improvement.
|
|
9
9
|
|
|
10
|
+
📚 **[Full documentation available at https://shokupan.dev](https://shokupan.dev)**
|
|
11
|
+
|
|
10
12
|
## ✨ Features
|
|
11
13
|
|
|
12
14
|
- 🎯 **TypeScript First** - End-to-end type safety with decorators and generics. No manual types needed.
|
|
@@ -75,6 +77,7 @@ That's it! Your server is running at `http://localhost:3000` 🎉
|
|
|
75
77
|
- [Using Express Middleware](#using-express-middleware)
|
|
76
78
|
- [Testing](#testing)
|
|
77
79
|
- [Deployment](#deployment)
|
|
80
|
+
- [Production Best Practices](https://knackstedt.github.io/shokupan/guides/production/) 📚
|
|
78
81
|
- [CLI Tools](#cli-tools)
|
|
79
82
|
- [API Reference](#api-reference)
|
|
80
83
|
- [Roadmap](#-roadmap)
|
|
@@ -929,7 +932,7 @@ This works great when combined with the Debug Dashboard.
|
|
|
929
932
|
A visual dashboard to inspect your application, view metrics, analyze the middleware graph, and replay failed requests.
|
|
930
933
|
|
|
931
934
|
```typescript
|
|
932
|
-
import { DebugDashboard } from 'shokupan
|
|
935
|
+
import { DebugDashboard } from 'shokupan';
|
|
933
936
|
|
|
934
937
|
// Mount the dashboard
|
|
935
938
|
app.mount('/debug', new DebugDashboard({
|
|
@@ -1057,8 +1060,8 @@ router.get('/wines/white', async (ctx) => {
|
|
|
1057
1060
|
router.get('/wines/all', async (ctx) => {
|
|
1058
1061
|
// Make parallel sub-requests
|
|
1059
1062
|
const [redResponse, whiteResponse] = await Promise.all([
|
|
1060
|
-
router.
|
|
1061
|
-
router.
|
|
1063
|
+
router.internalRequest('/wines/red'),
|
|
1064
|
+
router.internalRequest('/wines/white')
|
|
1062
1065
|
]);
|
|
1063
1066
|
|
|
1064
1067
|
const red = await redResponse.json();
|
|
@@ -1559,7 +1562,7 @@ describe('My App', () => {
|
|
|
1559
1562
|
app.get('/', () => ({ message: 'Hello' }));
|
|
1560
1563
|
|
|
1561
1564
|
// Process a request without starting the server
|
|
1562
|
-
const res = await app.
|
|
1565
|
+
const res = await app.testRequest({
|
|
1563
1566
|
method: 'GET',
|
|
1564
1567
|
path: '/'
|
|
1565
1568
|
});
|
|
@@ -1703,8 +1706,8 @@ const app = new Shokupan(config?: ShokupanConfig);
|
|
|
1703
1706
|
- `mount(path, controller)` - Mount controller or router
|
|
1704
1707
|
- `static(path, options)` - Serve static files
|
|
1705
1708
|
- `listen(port?)` - Start server
|
|
1706
|
-
- `
|
|
1707
|
-
- `
|
|
1709
|
+
- `testRequest(options)` - Process request (for testing purposes)
|
|
1710
|
+
- `internalRequest(options)` - Make sub-request
|
|
1708
1711
|
- `computeOpenAPISpec(base)` - Generate OpenAPI spec
|
|
1709
1712
|
|
|
1710
1713
|
### ShokupanRouter Class
|
|
@@ -1737,8 +1740,8 @@ const router = new ShokupanRouter(config?: ShokupanRouteConfig);
|
|
|
1737
1740
|
- `head(path, spec?, ...handlers)` - Add HEAD route
|
|
1738
1741
|
- `mount(path, controller)` - Mount controller or router
|
|
1739
1742
|
- `static(path, options)` - Serve static files
|
|
1740
|
-
- `
|
|
1741
|
-
- `
|
|
1743
|
+
- `testRequest(options)` - Process request (for testing purposes)
|
|
1744
|
+
- `internalRequest(options)` - Make sub-request
|
|
1742
1745
|
|
|
1743
1746
|
|
|
1744
1747
|
### ShokupanContext
|
package/dist/cli.cjs
CHANGED
|
@@ -4,7 +4,7 @@ const p = require("@clack/prompts");
|
|
|
4
4
|
const fs = require("node:fs");
|
|
5
5
|
const path = require("node:path");
|
|
6
6
|
const promises = require("node:timers/promises");
|
|
7
|
-
const openapiAnalyzer = require("./openapi-analyzer-
|
|
7
|
+
const openapiAnalyzer = require("./openapi-analyzer-Bei1sVWp.cjs");
|
|
8
8
|
function _interopNamespaceDefault(e) {
|
|
9
9
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
10
10
|
if (e) {
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as p from "@clack/prompts";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { setTimeout } from "node:timers/promises";
|
|
6
|
-
import { analyzeDirectory } from "./openapi-analyzer-
|
|
6
|
+
import { analyzeDirectory } from "./openapi-analyzer-Ce_7JxZh.js";
|
|
7
7
|
const templates = {
|
|
8
8
|
controller: (name) => `import { Controller, Get, Ctx } from 'shokupan';
|
|
9
9
|
import { ShokupanContext } from 'shokupan';
|
package/dist/context.d.ts
CHANGED
|
@@ -18,19 +18,89 @@ export interface DebugCollector {
|
|
|
18
18
|
setNode(id: string): void;
|
|
19
19
|
getCurrentNode(): string | undefined;
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Shokupan Request Context
|
|
23
|
+
*
|
|
24
|
+
* The context object passed to all middleware and route handlers.
|
|
25
|
+
* Provides access to request data, response helpers, and typed state management.
|
|
26
|
+
*
|
|
27
|
+
* @template State - The shape of `ctx.state` for type-safe state access across middleware.
|
|
28
|
+
* @template Params - The shape of `ctx.params` based on the route path pattern.
|
|
29
|
+
*
|
|
30
|
+
* @example Basic Usage
|
|
31
|
+
* ```typescript
|
|
32
|
+
* app.get('/hello', (ctx) => {
|
|
33
|
+
* return ctx.json({ message: 'Hello' });
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example Typed State
|
|
38
|
+
* ```typescript
|
|
39
|
+
* interface AppState {
|
|
40
|
+
* userId: string;
|
|
41
|
+
* requestId: string;
|
|
42
|
+
* }
|
|
43
|
+
*
|
|
44
|
+
* const app = new Shokupan<AppState>();
|
|
45
|
+
*
|
|
46
|
+
* app.use((ctx, next) => {
|
|
47
|
+
* ctx.state.requestId = crypto.randomUUID(); // ✓ Type-safe
|
|
48
|
+
* return next();
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @example Typed Path Parameters
|
|
53
|
+
* ```typescript
|
|
54
|
+
* app.get('/users/:userId/posts/:postId', (ctx) => {
|
|
55
|
+
* // ctx.params is automatically typed as { userId: string; postId: string }
|
|
56
|
+
* const { userId, postId } = ctx.params;
|
|
57
|
+
* return ctx.json({ userId, postId });
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example Full Type Safety (State + Params)
|
|
62
|
+
* ```typescript
|
|
63
|
+
* interface RequestState {
|
|
64
|
+
* userId: string;
|
|
65
|
+
* permissions: string[];
|
|
66
|
+
* }
|
|
67
|
+
*
|
|
68
|
+
* const app = new Shokupan<RequestState>();
|
|
69
|
+
*
|
|
70
|
+
* app.get('/admin/users/:userId', (ctx) => {
|
|
71
|
+
* // Both typed!
|
|
72
|
+
* const { userId } = ctx.params; // ✓ From path
|
|
73
|
+
* const { permissions } = ctx.state; // ✓ From state
|
|
74
|
+
*
|
|
75
|
+
* if (!permissions.includes('admin')) {
|
|
76
|
+
* return ctx.json({ error: 'Forbidden' }, 403);
|
|
77
|
+
* }
|
|
78
|
+
* return ctx.json({ userId });
|
|
79
|
+
* });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare class ShokupanContext<State extends Record<string, any> = Record<string, any>, Params extends Record<string, string> = Record<string, string>> {
|
|
22
83
|
readonly request: ShokupanRequest<any>;
|
|
23
84
|
readonly server?: Server;
|
|
24
85
|
readonly app?: Shokupan;
|
|
25
86
|
readonly signal?: AbortSignal;
|
|
26
|
-
|
|
27
|
-
params: Record<string, string>;
|
|
87
|
+
params: Params;
|
|
28
88
|
state: State;
|
|
29
89
|
handlerStack: HandlerStackItem[];
|
|
30
90
|
readonly response: ShokupanResponse;
|
|
31
91
|
_debug?: DebugCollector;
|
|
32
92
|
_finalResponse?: Response;
|
|
33
93
|
_rawBody?: string | ArrayBuffer | Uint8Array;
|
|
94
|
+
private _url?;
|
|
95
|
+
private _cachedBody?;
|
|
96
|
+
private _bodyType?;
|
|
97
|
+
private _bodyParsed;
|
|
98
|
+
_bodyParseError?: Error;
|
|
99
|
+
private _cachedHostname?;
|
|
100
|
+
private _cachedProtocol?;
|
|
101
|
+
private _cachedHost?;
|
|
102
|
+
private _cachedOrigin?;
|
|
103
|
+
private _cachedQuery?;
|
|
34
104
|
constructor(request: ShokupanRequest<any>, server?: Server, state?: State, app?: Shokupan, signal?: AbortSignal, // Optional as it might not be provided in tests or simple creates
|
|
35
105
|
enableMiddlewareTracking?: boolean);
|
|
36
106
|
get url(): URL;
|
|
@@ -101,6 +171,23 @@ export declare class ShokupanContext<State extends Record<string, any> = Record<
|
|
|
101
171
|
*/
|
|
102
172
|
setCookie(name: string, value: string, options?: CookieOptions): this;
|
|
103
173
|
private mergeHeaders;
|
|
174
|
+
/**
|
|
175
|
+
* Read request body with caching to avoid double parsing.
|
|
176
|
+
* The body is only parsed once and cached for subsequent reads.
|
|
177
|
+
*/
|
|
178
|
+
body<T = any>(): Promise<T>;
|
|
179
|
+
/**
|
|
180
|
+
* Pre-parse the request body before handler execution.
|
|
181
|
+
* This improves performance and enables Node.js compatibility for large payloads.
|
|
182
|
+
* Errors are deferred until the body is actually accessed in the handler.
|
|
183
|
+
*/
|
|
184
|
+
parseBody(): Promise<void>;
|
|
185
|
+
/**
|
|
186
|
+
* Read raw body from ReadableStream efficiently.
|
|
187
|
+
* This is much faster than request.text() for large payloads.
|
|
188
|
+
* Also handles the case where body is already a string (e.g., in tests).
|
|
189
|
+
*/
|
|
190
|
+
private readRawBody;
|
|
104
191
|
/**
|
|
105
192
|
* Send a response
|
|
106
193
|
* @param body Response body
|
|
@@ -108,10 +195,6 @@ export declare class ShokupanContext<State extends Record<string, any> = Record<
|
|
|
108
195
|
* @returns Response
|
|
109
196
|
*/
|
|
110
197
|
send(body?: BodyInit, options?: ResponseInit): Response;
|
|
111
|
-
/**
|
|
112
|
-
* Read request body
|
|
113
|
-
*/
|
|
114
|
-
body<T = any>(): Promise<T>;
|
|
115
198
|
/**
|
|
116
199
|
* Respond with a JSON object
|
|
117
200
|
*/
|