shokupan 0.4.5 → 0.6.0
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 +10 -9
- package/dist/analysis/openapi-analyzer.d.ts +0 -4
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/context.d.ts +30 -8
- package/dist/index.cjs +692 -461
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +635 -426
- 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-D9YB3IkV.cjs → openapi-analyzer-Bei1sVWp.cjs} +63 -49
- package/dist/openapi-analyzer-Bei1sVWp.cjs.map +1 -0
- package/dist/{openapi-analyzer-BtIaHIfe.js → openapi-analyzer-Ce_7JxZh.js} +63 -49
- package/dist/openapi-analyzer-Ce_7JxZh.js.map +1 -0
- package/dist/plugins/scalar.d.ts +1 -1
- package/dist/router.d.ts +33 -22
- 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 +4 -8
- package/dist/types.d.ts +32 -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 +74 -14
- package/dist/benchmarking/advanced-cases/elysia.d.ts +0 -1
- package/dist/benchmarking/advanced-cases/express.d.ts +0 -1
- package/dist/benchmarking/advanced-cases/fastify.d.ts +0 -1
- package/dist/benchmarking/advanced-cases/hapi.d.ts +0 -1
- package/dist/benchmarking/advanced-cases/hono.d.ts +0 -1
- package/dist/benchmarking/advanced-cases/koa.d.ts +0 -1
- package/dist/benchmarking/advanced-cases/nest.d.ts +0 -1
- package/dist/benchmarking/advanced-cases/shokupan.d.ts +0 -1
- package/dist/benchmarking/advanced-data.d.ts +0 -33
- package/dist/benchmarking/advanced-runner.d.ts +0 -1
- package/dist/benchmarking/advanced-worker.d.ts +0 -0
- package/dist/benchmarking/cases/elysia.d.ts +0 -1
- package/dist/benchmarking/cases/express.d.ts +0 -1
- package/dist/benchmarking/cases/fastify.d.ts +0 -1
- package/dist/benchmarking/cases/hapi.d.ts +0 -1
- package/dist/benchmarking/cases/hono.d.ts +0 -1
- package/dist/benchmarking/cases/koa.d.ts +0 -1
- package/dist/benchmarking/cases/nest.d.ts +0 -1
- package/dist/benchmarking/cases/shokupan.d.ts +0 -1
- package/dist/benchmarking/data.d.ts +0 -15
- package/dist/benchmarking/quick_bench.d.ts +0 -1
- package/dist/benchmarking/runner.d.ts +0 -1
- package/dist/benchmarking/worker.d.ts +0 -0
- package/dist/buntest.d.ts +0 -1
- package/dist/openapi-analyzer-BtIaHIfe.js.map +0 -1
- package/dist/openapi-analyzer-D9YB3IkV.cjs.map +0 -1
- package/dist/server-adapter-BWrEJbKL.js.map +0 -1
- package/dist/server-adapter-fVKP60e0.cjs.map +0 -1
|
@@ -13,7 +13,9 @@ function createHttpServer() {
|
|
|
13
13
|
req.on("end", () => controller.close());
|
|
14
14
|
req.on("error", (err) => controller.error(err));
|
|
15
15
|
}
|
|
16
|
-
})
|
|
16
|
+
}),
|
|
17
|
+
// Required for Node.js undici when sending a body
|
|
18
|
+
duplex: "half"
|
|
17
19
|
});
|
|
18
20
|
const response = await options.fetch(request, fauxServer);
|
|
19
21
|
res.statusCode = response.status;
|
|
@@ -61,4 +63,4 @@ function createHttpServer() {
|
|
|
61
63
|
export {
|
|
62
64
|
createHttpServer
|
|
63
65
|
};
|
|
64
|
-
//# sourceMappingURL=server-adapter-
|
|
66
|
+
//# sourceMappingURL=server-adapter-0xH174zz.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-adapter-0xH174zz.js","sources":["../src/plugins/server-adapter.ts"],"sourcesContent":["import type { Server } from \"bun\";\nimport * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport type { ServerFactory } from \"../types\";\n\n/**\n * Creates a server factory that uses the standard Node.js `http` module.\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpServer(): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url!, `http://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any,\n // Required for Node.js undici when sending a body\n duplex: 'half'\n } as any);\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // Optimize: Use arrayBuffer for direct conversion instead of async iteration\n const buffer = await response.arrayBuffer();\n res.end(Buffer.from(buffer));\n } else {\n res.end();\n }\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n return Promise.resolve(); // Bun.Server stop usually returns void but in type definition it might vary.\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`http://${options.hostname}:${options.port}`)\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n\n/**\n * Creates a server factory that uses the standard Node.js `https` module.\n * @param sslOptions - Node.js HTTPS options (key, cert, etc.)\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpsServer(sslOptions: https.ServerOptions): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = https.createServer(sslOptions, async (req, res) => {\n const url = new URL(req.url!, `https://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any,\n // Required for Node.js undici when sending a body\n duplex: 'half'\n } as any);\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // Optimize: Use arrayBuffer for direct conversion instead of async iteration\n const buffer = await response.arrayBuffer();\n res.end(Buffer.from(buffer));\n } else {\n res.end();\n }\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`https://${options.hostname}:${options.port}`)\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n"],"names":["options"],"mappings":";;AASO,SAAS,mBAAkC;AAC9C,SAAO,OAAO,YAAkC;AAC5C,UAAM,SAAS,KAAK,aAAa,OAAO,KAAK,QAAQ;AACjD,YAAM,MAAM,IAAI,IAAI,IAAI,KAAM,UAAU,IAAI,QAAQ,IAAI,EAAE;AAC1D,YAAM,UAAU,IAAI,QAAQ,IAAI,YAAY;AAAA,QACxC,QAAQ,IAAI;AAAA,QACZ,SAAS,IAAI;AAAA,QACb,MAAM,CAAC,OAAO,MAAM,EAAE,SAAS,IAAI,MAAO,IAAI,SAAY,IAAI,eAAe;AAAA,UACzE,MAAM,YAAY;AACd,gBAAI,GAAG,QAAQ,CAAA,UAAS,WAAW,QAAQ,KAAK,CAAC;AACjD,gBAAI,GAAG,OAAO,MAAM,WAAW,OAAO;AACtC,gBAAI,GAAG,SAAS,CAAA,QAAO,WAAW,MAAM,GAAG,CAAC;AAAA,UAChD;AAAA,QAAA,CACH;AAAA;AAAA,QAED,QAAQ;AAAA,MAAA,CACJ;AAER,YAAM,WAAW,MAAM,QAAQ,MAAM,SAAS,UAAU;AAExD,UAAI,aAAa,SAAS;AAC1B,eAAS,QAAQ,QAAQ,CAAC,GAAG,MAAM,IAAI,UAAU,GAAG,CAAC,CAAC;AAEtD,UAAI,SAAS,MAAM;AAEf,cAAM,SAAS,MAAM,SAAS,YAAA;AAC9B,YAAI,IAAI,OAAO,KAAK,MAAM,CAAC;AAAA,MAC/B,OAAO;AACH,YAAI,IAAA;AAAA,MACR;AAAA,IACJ,CAAC;AAED,UAAM,aAAqB;AAAA,MACvB,MAAM,MAAM;AACR,eAAO,MAAA;AACP,eAAO,QAAQ,QAAA;AAAA,MACnB;AAAA,MACA,QAAQ,KAAKA,UAAS;AAClB,eAAO;AAAA,MACX;AAAA,MACA,OAAOA,UAAS;AACZ,eAAO;AAAA,MACX;AAAA,MACA,IAAI,OAAO;AACP,cAAM,OAAO,OAAO,QAAA;AACpB,YAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC3C,iBAAO,KAAK;AAAA,QAChB;AACA,eAAO,QAAQ;AAAA,MACnB;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,aAAa,QAAQ;AAAA,MACrB,iBAAiB;AAAA,MACjB,WAAW,CAAC,QAAQ;AAAA,MACpB,SAAS,MAAM;AAAA,MACf,iBAAiB,MAAM;AAAA,MACvB,KAAK,IAAI,IAAI,UAAU,QAAQ,QAAQ,IAAI,QAAQ,IAAI,EAAE;AAAA,IAAA;AAG7D,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC5B,aAAO,OAAO,QAAQ,MAAM,QAAQ,UAAU,MAAM;AAChD,gBAAQ,UAAU;AAAA,MACtB,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AACJ;"}
|
|
@@ -32,7 +32,9 @@ function createHttpServer() {
|
|
|
32
32
|
req.on("end", () => controller.close());
|
|
33
33
|
req.on("error", (err) => controller.error(err));
|
|
34
34
|
}
|
|
35
|
-
})
|
|
35
|
+
}),
|
|
36
|
+
// Required for Node.js undici when sending a body
|
|
37
|
+
duplex: "half"
|
|
36
38
|
});
|
|
37
39
|
const response = await options.fetch(request, fauxServer);
|
|
38
40
|
res.statusCode = response.status;
|
|
@@ -78,4 +80,4 @@ function createHttpServer() {
|
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
exports.createHttpServer = createHttpServer;
|
|
81
|
-
//# sourceMappingURL=server-adapter-
|
|
83
|
+
//# sourceMappingURL=server-adapter-DFhwlK8e.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-adapter-DFhwlK8e.cjs","sources":["../src/plugins/server-adapter.ts"],"sourcesContent":["import type { Server } from \"bun\";\nimport * as http from \"node:http\";\nimport * as https from \"node:https\";\nimport type { ServerFactory } from \"../types\";\n\n/**\n * Creates a server factory that uses the standard Node.js `http` module.\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpServer(): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = http.createServer(async (req, res) => {\n const url = new URL(req.url!, `http://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any,\n // Required for Node.js undici when sending a body\n duplex: 'half'\n } as any);\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // Optimize: Use arrayBuffer for direct conversion instead of async iteration\n const buffer = await response.arrayBuffer();\n res.end(Buffer.from(buffer));\n } else {\n res.end();\n }\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n return Promise.resolve(); // Bun.Server stop usually returns void but in type definition it might vary.\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`http://${options.hostname}:${options.port}`)\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n\n/**\n * Creates a server factory that uses the standard Node.js `https` module.\n * @param sslOptions - Node.js HTTPS options (key, cert, etc.)\n * @returns A ServerFactory compatible with Shokupan.\n */\nexport function createHttpsServer(sslOptions: https.ServerOptions): ServerFactory {\n return async (options: any): Promise<Server> => {\n const server = https.createServer(sslOptions, async (req, res) => {\n const url = new URL(req.url!, `https://${req.headers.host}`);\n const request = new Request(url.toString(), {\n method: req.method,\n headers: req.headers as any,\n body: ['GET', 'HEAD'].includes(req.method!) ? undefined : new ReadableStream({\n start(controller) {\n req.on('data', chunk => controller.enqueue(chunk));\n req.on('end', () => controller.close());\n req.on('error', err => controller.error(err));\n }\n }) as any,\n // Required for Node.js undici when sending a body\n duplex: 'half'\n } as any);\n\n const response = await options.fetch(request, fauxServer);\n\n res.statusCode = response.status;\n response.headers.forEach((v, k) => res.setHeader(k, v));\n\n if (response.body) {\n // Optimize: Use arrayBuffer for direct conversion instead of async iteration\n const buffer = await response.arrayBuffer();\n res.end(Buffer.from(buffer));\n } else {\n res.end();\n }\n });\n\n const fauxServer: Server = {\n stop: () => {\n server.close();\n },\n upgrade(req, options) {\n return false;\n },\n reload(options) {\n return fauxServer as any;\n },\n get port() {\n const addr = server.address();\n if (typeof addr === 'object' && addr !== null) {\n return addr.port;\n }\n return options.port;\n },\n hostname: options.hostname,\n development: options.development,\n pendingRequests: 0,\n requestIP: (req) => null,\n publish: () => 0,\n subscriberCount: () => 0,\n url: new URL(`https://${options.hostname}:${options.port}`)\n } as unknown as Server;\n\n return new Promise((resolve) => {\n server.listen(options.port, options.hostname, () => {\n resolve(fauxServer);\n });\n });\n };\n}\n"],"names":["http","options"],"mappings":";;;;;;;;;;;;;;;;;;;;;AASO,SAAS,mBAAkC;AAC9C,SAAO,OAAO,YAAkC;AAC5C,UAAM,SAASA,gBAAK,aAAa,OAAO,KAAK,QAAQ;AACjD,YAAM,MAAM,IAAI,IAAI,IAAI,KAAM,UAAU,IAAI,QAAQ,IAAI,EAAE;AAC1D,YAAM,UAAU,IAAI,QAAQ,IAAI,YAAY;AAAA,QACxC,QAAQ,IAAI;AAAA,QACZ,SAAS,IAAI;AAAA,QACb,MAAM,CAAC,OAAO,MAAM,EAAE,SAAS,IAAI,MAAO,IAAI,SAAY,IAAI,eAAe;AAAA,UACzE,MAAM,YAAY;AACd,gBAAI,GAAG,QAAQ,CAAA,UAAS,WAAW,QAAQ,KAAK,CAAC;AACjD,gBAAI,GAAG,OAAO,MAAM,WAAW,OAAO;AACtC,gBAAI,GAAG,SAAS,CAAA,QAAO,WAAW,MAAM,GAAG,CAAC;AAAA,UAChD;AAAA,QAAA,CACH;AAAA;AAAA,QAED,QAAQ;AAAA,MAAA,CACJ;AAER,YAAM,WAAW,MAAM,QAAQ,MAAM,SAAS,UAAU;AAExD,UAAI,aAAa,SAAS;AAC1B,eAAS,QAAQ,QAAQ,CAAC,GAAG,MAAM,IAAI,UAAU,GAAG,CAAC,CAAC;AAEtD,UAAI,SAAS,MAAM;AAEf,cAAM,SAAS,MAAM,SAAS,YAAA;AAC9B,YAAI,IAAI,OAAO,KAAK,MAAM,CAAC;AAAA,MAC/B,OAAO;AACH,YAAI,IAAA;AAAA,MACR;AAAA,IACJ,CAAC;AAED,UAAM,aAAqB;AAAA,MACvB,MAAM,MAAM;AACR,eAAO,MAAA;AACP,eAAO,QAAQ,QAAA;AAAA,MACnB;AAAA,MACA,QAAQ,KAAKC,UAAS;AAClB,eAAO;AAAA,MACX;AAAA,MACA,OAAOA,UAAS;AACZ,eAAO;AAAA,MACX;AAAA,MACA,IAAI,OAAO;AACP,cAAM,OAAO,OAAO,QAAA;AACpB,YAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC3C,iBAAO,KAAK;AAAA,QAChB;AACA,eAAO,QAAQ;AAAA,MACnB;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,aAAa,QAAQ;AAAA,MACrB,iBAAiB;AAAA,MACjB,WAAW,CAAC,QAAQ;AAAA,MACpB,SAAS,MAAM;AAAA,MACf,iBAAiB,MAAM;AAAA,MACvB,KAAK,IAAI,IAAI,UAAU,QAAQ,QAAQ,IAAI,QAAQ,IAAI,EAAE;AAAA,IAAA;AAG7D,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC5B,aAAO,OAAO,QAAQ,MAAM,QAAQ,UAAU,MAAM;AAChD,gBAAQ,UAAU;AAAA,MACtB,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AACJ;;"}
|
package/dist/shokupan.d.ts
CHANGED
|
@@ -2,13 +2,12 @@ import { ShokupanRequest } from './request';
|
|
|
2
2
|
import { ShokupanRouter } from './router';
|
|
3
3
|
import { $dispatch } from './symbol';
|
|
4
4
|
import { Middleware, ProcessResult, RequestOptions, ShokupanConfig } from './types';
|
|
5
|
+
import { Server } from 'bun';
|
|
5
6
|
export declare class Shokupan<T = any> extends ShokupanRouter<T> {
|
|
6
7
|
readonly applicationConfig: ShokupanConfig;
|
|
7
8
|
openApiSpec?: any;
|
|
8
9
|
private composedMiddleware?;
|
|
9
10
|
private cpuMonitor?;
|
|
10
|
-
private hookCache;
|
|
11
|
-
private hooksInitialized;
|
|
12
11
|
get logger(): {
|
|
13
12
|
verbose?: boolean;
|
|
14
13
|
info?: (msg: string, props: Record<string, any>) => void;
|
|
@@ -39,12 +38,12 @@ export declare class Shokupan<T = any> extends ShokupanRouter<T> {
|
|
|
39
38
|
* @param port - The port to listen on. If not specified, the port from the configuration is used. If that is not specified, port 3000 is used.
|
|
40
39
|
* @returns The server instance.
|
|
41
40
|
*/
|
|
42
|
-
listen(port?: number): Promise<
|
|
41
|
+
listen(port?: number): Promise<Server | import('http').Server<typeof import('http').IncomingMessage, typeof import('http').ServerResponse>>;
|
|
43
42
|
[$dispatch](req: ShokupanRequest<T>): Promise<Response>;
|
|
44
43
|
/**
|
|
45
44
|
* Processes a request by wrapping the standard fetch method.
|
|
46
45
|
*/
|
|
47
|
-
|
|
46
|
+
testRequest(options: RequestOptions): Promise<ProcessResult>;
|
|
48
47
|
/**
|
|
49
48
|
* Handles an incoming request (Bun.serve interface).
|
|
50
49
|
* This logic contains the middleware chain and router dispatch.
|
|
@@ -53,9 +52,6 @@ export declare class Shokupan<T = any> extends ShokupanRouter<T> {
|
|
|
53
52
|
* @param server - The server instance.
|
|
54
53
|
* @returns The response to send.
|
|
55
54
|
*/
|
|
56
|
-
fetch(req: Request, server?:
|
|
55
|
+
fetch(req: Request, server?: Server): Promise<Response>;
|
|
57
56
|
private handleRequest;
|
|
58
|
-
private ensureHooksInitialized;
|
|
59
|
-
private executeHook;
|
|
60
|
-
private hasHook;
|
|
61
57
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OpenAPI } from '@scalar/openapi-types';
|
|
2
2
|
import { Server } from 'bun';
|
|
3
|
+
import { Server as NodeServer } from 'node:http';
|
|
3
4
|
import { ShokupanContext } from './context';
|
|
4
5
|
import { $isRouter } from './symbol';
|
|
5
6
|
export type DeepPartial<T> = T extends Function ? T : T extends object ? {
|
|
@@ -27,7 +28,7 @@ export interface OpenAPIOptions {
|
|
|
27
28
|
defaultTag?: string;
|
|
28
29
|
}
|
|
29
30
|
export interface ShokupanHooks<T = any> {
|
|
30
|
-
onError?: (
|
|
31
|
+
onError?: (ctx: ShokupanContext<T>, error: unknown) => void | Promise<void>;
|
|
31
32
|
onRequestStart?: (ctx: ShokupanContext<T>) => void | Promise<void>;
|
|
32
33
|
onRequestEnd?: (ctx: ShokupanContext<T>) => void | Promise<void>;
|
|
33
34
|
onResponseStart?: (ctx: ShokupanContext<T>, response: Response) => void | Promise<void>;
|
|
@@ -60,7 +61,7 @@ export declare enum RouteParamType {
|
|
|
60
61
|
CONTEXT = "CONTEXT"
|
|
61
62
|
}
|
|
62
63
|
export interface ServerFactory {
|
|
63
|
-
(options: any): Server<
|
|
64
|
+
(options: any): Server | Promise<Server> | NodeServer | Promise<NodeServer>;
|
|
64
65
|
}
|
|
65
66
|
export type NextFn = () => Promise<any>;
|
|
66
67
|
export type Middleware = ((ctx: ShokupanContext<unknown>, next: NextFn) => Promise<any> | any) & {
|
|
@@ -88,10 +89,12 @@ export type ShokupanRouteConfig = DeepPartial<{
|
|
|
88
89
|
hooks: ShokupanHooks | ShokupanHooks[];
|
|
89
90
|
/**
|
|
90
91
|
* Whether to enforce that only controller classes (constructors) are accepted by the router.
|
|
92
|
+
* @default false
|
|
91
93
|
*/
|
|
92
94
|
controllersOnly: boolean;
|
|
93
95
|
/**
|
|
94
96
|
* Whether to enable automatic backpressure based on system CPU load.
|
|
97
|
+
* @default false
|
|
95
98
|
*/
|
|
96
99
|
autoBackpressureFeedback: boolean;
|
|
97
100
|
/**
|
|
@@ -213,13 +216,29 @@ export type ShokupanConfig<T extends Record<string, any> = Record<string, any>>
|
|
|
213
216
|
* @default false
|
|
214
217
|
*/
|
|
215
218
|
enableTracing?: boolean;
|
|
219
|
+
/**
|
|
220
|
+
* JSON parser to use for parsing request bodies.
|
|
221
|
+
*
|
|
222
|
+
* Options:
|
|
223
|
+
* - `'native'`: Use the built-in JSON.parse (fastest, default)
|
|
224
|
+
* - `'parse-json'`: Use the parse-json library for better error messages with minimal performance overhead (~5% slower than native)
|
|
225
|
+
* - `'secure-json-parse'`: Use secure-json-parse for protection against prototype pollution (20-30% slower than native)
|
|
226
|
+
*
|
|
227
|
+
* Performance implications based on benchmarks:
|
|
228
|
+
* - `native`: Fastest option, excellent for production
|
|
229
|
+
* - `parse-json`: Nearly identical performance to native with better error messages, good for development
|
|
230
|
+
* - `secure-json-parse`: Provides security at the cost of performance, use only for untrusted input
|
|
231
|
+
*
|
|
232
|
+
* @default 'native'
|
|
233
|
+
*/
|
|
234
|
+
jsonParser?: 'native' | 'parse-json' | 'secure-json-parse';
|
|
216
235
|
/**
|
|
217
236
|
* Whether to enable automatic backpressure based on system CPU load.
|
|
218
237
|
* @default false
|
|
219
238
|
*/
|
|
220
239
|
autoBackpressureFeedback?: boolean;
|
|
221
240
|
/**
|
|
222
|
-
* The CPU usage percentage threshold (0-100) at which to start rejecting requests.
|
|
241
|
+
* The CPU usage percentage threshold (0-100) at which to start rejecting requests (429).
|
|
223
242
|
* @default 60
|
|
224
243
|
*/
|
|
225
244
|
autoBackpressureLevel?: number;
|
|
@@ -276,6 +295,7 @@ export type ShokupanConfig<T extends Record<string, any> = Record<string, any>>
|
|
|
276
295
|
/**
|
|
277
296
|
* Timeout for writing the response (milliseconds).
|
|
278
297
|
* Not currently supported by Bun.serve natively.
|
|
298
|
+
* @experimental
|
|
279
299
|
*/
|
|
280
300
|
writeTimeout: number;
|
|
281
301
|
/**
|
|
@@ -291,6 +311,15 @@ export type ShokupanConfig<T extends Record<string, any> = Record<string, any>>
|
|
|
291
311
|
* Lifecycle hooks.
|
|
292
312
|
*/
|
|
293
313
|
hooks: ShokupanHooks<T> | ShokupanHooks<T>[];
|
|
314
|
+
/**
|
|
315
|
+
* Whether to validate response status codes.
|
|
316
|
+
* @default true
|
|
317
|
+
*/
|
|
318
|
+
validateStatusCodes: boolean;
|
|
319
|
+
/**
|
|
320
|
+
* Any other config options are allowed, but will be ignored.
|
|
321
|
+
* @deprecated
|
|
322
|
+
*/
|
|
294
323
|
[key: string]: any;
|
|
295
324
|
}>;
|
|
296
325
|
export interface RequestOptions {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const datastore: {
|
|
2
|
+
get<T extends Record<string, any>>(store: string, key: string): Promise<T>;
|
|
3
|
+
set(store: string, key: string, value: any): Promise<any>;
|
|
4
|
+
query(query: string, vars?: Record<string, unknown>): Promise<any>;
|
|
5
|
+
readonly ready: Promise<any>;
|
|
6
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON parser utilities for Shokupan
|
|
3
|
+
* Supports multiple JSON parsing libraries with different performance characteristics
|
|
4
|
+
*/
|
|
5
|
+
type JSONParser = (text: string) => any;
|
|
6
|
+
/**
|
|
7
|
+
* Get the appropriate JSON parser based on the configuration
|
|
8
|
+
* @param parserType - The type of parser to use
|
|
9
|
+
* @returns A JSON parsing function
|
|
10
|
+
*/
|
|
11
|
+
export declare function getJSONParser(parserType?: 'native' | 'parse-json' | 'secure-json-parse'): JSONParser;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin dependency loader with helpful error messages
|
|
3
|
+
* Provides lazy loading for optional plugin dependencies
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Loads a plugin dependency with helpful error message if not installed
|
|
7
|
+
* @param packageName The npm package to load
|
|
8
|
+
* @param pluginName The shokupan plugin that requires it
|
|
9
|
+
* @param installCommand Optional custom install command
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadPluginDependency(packageName: string, pluginName: string, installCommand?: string): Promise<any>;
|
|
12
|
+
/**
|
|
13
|
+
* Loads multiple plugin dependencies
|
|
14
|
+
* @param dependencies Array of dependency info
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadPluginDependencies(dependencies: Array<{
|
|
17
|
+
package: string;
|
|
18
|
+
plugin: string;
|
|
19
|
+
installCommand?: string;
|
|
20
|
+
}>): Promise<Record<string, any>>;
|
|
21
|
+
/**
|
|
22
|
+
* Checks if a package is available without throwing
|
|
23
|
+
* @param packageName The npm package to check
|
|
24
|
+
*/
|
|
25
|
+
export declare function isPackageAvailable(packageName: string): Promise<boolean>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shokupan",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Shokupan is a low-lift modern web framework for Bun.",
|
|
5
5
|
"author": "Andrew G. Knackstedt",
|
|
6
6
|
"publishConfig": {
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"docs": "cd docs && bun run dev",
|
|
28
28
|
"build": "vite build",
|
|
29
29
|
"bench": "cd src/benchmarking && bun runner.ts",
|
|
30
|
-
"bench:advanced": "cd src/benchmarking && bun advanced-runner.ts"
|
|
30
|
+
"bench:advanced": "cd src/benchmarking && bun advanced-runner.ts",
|
|
31
|
+
"retag": "git push origin :refs/tags/v$(node -p \"require('./package.json').version\") 2>/dev/null || true && git tag -d v$(node -p \"require('./package.json').version\") 2>/dev/null || true && git tag v$(node -p \"require('./package.json').version\") && git push origin v$(node -p \"require('./package.json').version\") --force"
|
|
31
32
|
},
|
|
32
33
|
"bin": {
|
|
33
34
|
"shokupan": "dist/cli.js",
|
|
@@ -63,33 +64,92 @@
|
|
|
63
64
|
"@opentelemetry/sdk-trace-base": "^2.2.0",
|
|
64
65
|
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
|
65
66
|
"@opentelemetry/semantic-conventions": "^1.38.0",
|
|
66
|
-
"@scalar/api-reference": "^1.40.9",
|
|
67
67
|
"@scalar/openapi-types": "^0.5.3",
|
|
68
|
-
"@surrealdb/node": "^2.4.0",
|
|
69
|
-
"ajv": "^8.17.1",
|
|
70
|
-
"ajv-formats": "^3.0.1",
|
|
71
|
-
"arctic": "^3.7.0",
|
|
72
|
-
"class-transformer": "^0.5.1",
|
|
73
|
-
"class-validator": "^0.14.3",
|
|
74
|
-
"eta": "^4.5.0",
|
|
75
|
-
"jose": "^6.1.3",
|
|
76
|
-
"reflect-metadata": "^0.2.2",
|
|
77
|
-
"surrealdb": "^2.0.0-alpha.14",
|
|
78
68
|
"tslib": "^2.8.1"
|
|
79
69
|
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"ajv": "^8.0.0",
|
|
72
|
+
"ajv-formats": "^3.0.0",
|
|
73
|
+
"arctic": "^3",
|
|
74
|
+
"class-transformer": "^0.5.0",
|
|
75
|
+
"class-validator": "^0.14.0",
|
|
76
|
+
"eta": "^4.0.0",
|
|
77
|
+
"jose": "^6.0.0",
|
|
78
|
+
"parse-json": "^8.0.0",
|
|
79
|
+
"reflect-metadata": "^0.2.0",
|
|
80
|
+
"secure-json-parse": "^4.0.0",
|
|
81
|
+
"@scalar/api-reference": "^1.0.0",
|
|
82
|
+
"@surrealdb/node": "^2.4.0",
|
|
83
|
+
"surrealdb": "^2.0.0-alpha.14"
|
|
84
|
+
},
|
|
85
|
+
"peerDependenciesMeta": {
|
|
86
|
+
"ajv": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"ajv-formats": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"arctic": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"class-transformer": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"class-validator": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"eta": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"jose": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"parse-json": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"reflect-metadata": {
|
|
111
|
+
"optional": true
|
|
112
|
+
},
|
|
113
|
+
"secure-json-parse": {
|
|
114
|
+
"optional": true
|
|
115
|
+
},
|
|
116
|
+
"@scalar/api-reference": {
|
|
117
|
+
"optional": true
|
|
118
|
+
},
|
|
119
|
+
"@surrealdb/node": {
|
|
120
|
+
"optional": true
|
|
121
|
+
},
|
|
122
|
+
"surrealdb": {
|
|
123
|
+
"optional": true
|
|
124
|
+
}
|
|
125
|
+
},
|
|
80
126
|
"devDependencies": {
|
|
127
|
+
"@scalar/api-reference": "^1.0.0",
|
|
81
128
|
"@sinclair/typebox": "^0.34.45",
|
|
129
|
+
"@surrealdb/node": "^2.4.0",
|
|
82
130
|
"@tsconfig/bun": "^1.0.8",
|
|
83
131
|
"@types/axios": "^0.9.36",
|
|
84
132
|
"@types/bun": "^1.2.23",
|
|
85
133
|
"@types/supertest": "^6.0.3",
|
|
134
|
+
"ajv": "^8.0.0",
|
|
135
|
+
"ajv-formats": "^3.0.0",
|
|
136
|
+
"arctic": "^3",
|
|
86
137
|
"axios": "^1.13.2",
|
|
138
|
+
"class-transformer": "^0.5.0",
|
|
139
|
+
"class-validator": "^0.14.0",
|
|
140
|
+
"eta": "^4.0.0",
|
|
141
|
+
"fast-json-stringify": "^6.1.1",
|
|
87
142
|
"get-port": "^7.1.0",
|
|
143
|
+
"jose": "^6.0.0",
|
|
144
|
+
"parse-json": "^8.0.0",
|
|
145
|
+
"reflect-metadata": "^0.2.0",
|
|
146
|
+
"secure-json-parse": "^4.0.0",
|
|
88
147
|
"supertest": "^7.1.4",
|
|
148
|
+
"surrealdb": "^2.0.0-alpha.14",
|
|
89
149
|
"typescript": "~5.9.3",
|
|
90
150
|
"valibot": "^1.2.0",
|
|
91
151
|
"vite": "^7.3.0",
|
|
92
152
|
"vite-plugin-dts": "^4.5.4",
|
|
93
153
|
"zod": "^4.2.1"
|
|
94
154
|
}
|
|
95
|
-
}
|
|
155
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function startAdvanced(port: number, scenario: string): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function startAdvanced(port: number, scenario: string): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function startAdvanced(port: number, scenario: string): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function startAdvanced(port: number, scenario: string): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function startAdvanced(port: number, scenario: string): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function startAdvanced(port: number, scenario: string): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function startAdvanced(port: number, scenario: string): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function startAdvanced(port: number, scenario: string): Promise<() => Promise<void>>;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export declare const LARGE_JSON: {
|
|
2
|
-
total: number;
|
|
3
|
-
items: any[];
|
|
4
|
-
metadata: {
|
|
5
|
-
generated: string;
|
|
6
|
-
size: string;
|
|
7
|
-
purpose: string;
|
|
8
|
-
};
|
|
9
|
-
};
|
|
10
|
-
export declare const LARGE_REQUEST_BODY: string;
|
|
11
|
-
export declare const LARGE_HEADERS: Record<string, string>;
|
|
12
|
-
/**
|
|
13
|
-
* Calculate MD5 hash of a string
|
|
14
|
-
*/
|
|
15
|
-
export declare function md5(input: string): string;
|
|
16
|
-
/**
|
|
17
|
-
* Serialize request data for hashing
|
|
18
|
-
*/
|
|
19
|
-
export declare function serializeRequest(url: string, headers: Record<string, string>, body: string): string;
|
|
20
|
-
/**
|
|
21
|
-
* Get the actual byte size of the LARGE_JSON
|
|
22
|
-
*/
|
|
23
|
-
export declare function getLargeJSONSize(): number;
|
|
24
|
-
/**
|
|
25
|
-
* Smaller compressible response (100KB) for compression testing
|
|
26
|
-
*/
|
|
27
|
-
export declare const COMPRESSIBLE_JSON: {
|
|
28
|
-
data: {
|
|
29
|
-
id: number;
|
|
30
|
-
text: string;
|
|
31
|
-
timestamp: string;
|
|
32
|
-
}[];
|
|
33
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function start(port: number): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function start(port: number): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function start(port: number): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function start(port: number): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function start(port: number): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function start(port: number): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function start(port: number): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function start(port: number): Promise<() => Promise<void>>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
File without changes
|
package/dist/buntest.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|