shokupan 0.0.1 → 0.2.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 +13 -12
- package/dist/analysis/openapi-analyzer.d.ts +142 -0
- package/dist/cli.cjs +62 -2
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +62 -2
- package/dist/cli.js.map +1 -1
- package/dist/context.d.ts +59 -5
- package/dist/decorators.d.ts +5 -1
- package/dist/index.cjs +1799 -737
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1799 -737
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +2 -0
- package/dist/openapi-analyzer-BN0wFCML.cjs +772 -0
- package/dist/openapi-analyzer-BN0wFCML.cjs.map +1 -0
- package/dist/openapi-analyzer-BTExMLX4.js +772 -0
- package/dist/openapi-analyzer-BTExMLX4.js.map +1 -0
- package/dist/plugins/debugview/plugin.d.ts +34 -0
- package/dist/plugins/openapi-validator.d.ts +2 -0
- package/dist/plugins/openapi.d.ts +10 -0
- package/dist/plugins/proxy.d.ts +9 -0
- package/dist/plugins/scalar.d.ts +3 -1
- package/dist/plugins/serve-static.d.ts +3 -0
- package/dist/plugins/server-adapter.d.ts +13 -0
- package/dist/response.d.ts +4 -0
- package/dist/router.d.ts +43 -15
- package/dist/server-adapter-BD6oKEto.cjs +81 -0
- package/dist/server-adapter-BD6oKEto.cjs.map +1 -0
- package/dist/server-adapter-CnQFr4P7.js +64 -0
- package/dist/server-adapter-CnQFr4P7.js.map +1 -0
- package/dist/shokupan.d.ts +16 -7
- package/dist/symbol.d.ts +2 -0
- package/dist/types.d.ts +120 -1
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -1628,21 +1628,22 @@ Container.clear();
|
|
|
1628
1628
|
- ✅ **OpenTelemetry** - Built-in [OpenTelemetry](https://opentelemetry.io/) traces
|
|
1629
1629
|
- ✅ **OAuth2** - Built-in [OAuth2](https://oauth.net/2/) support
|
|
1630
1630
|
- ✅ **Request-Scoped Globals** - Request-scoped values via [AsyncLocalStorage](https://docs.deno.com/api/node/async_hooks/~/AsyncLocalStorage)
|
|
1631
|
-
|
|
1631
|
+
- ✅ **Runtime Compatibility** - Support for [Deno](https://deno.com/) and [Node.js](https://nodejs.org/)
|
|
1632
|
+
- ✅ **Deep Introspection** - Type analysis for enhanced OpenAPI generation
|
|
1633
|
+
- ✅ **Controller Mode** - Option for controller-only mode
|
|
1634
|
+
- ✅ **Supports Node/Deno** - Shokupan can run on Node.js or Deno
|
|
1635
|
+
- ✅ **OpenAPI Validation** - Built-in [OpenAPI](https://www.openapis.org/) validation
|
|
1632
1636
|
|
|
1633
1637
|
### Future Features
|
|
1634
1638
|
|
|
1635
|
-
-
|
|
1636
|
-
-
|
|
1637
|
-
-
|
|
1638
|
-
-
|
|
1639
|
-
-
|
|
1640
|
-
-
|
|
1641
|
-
-
|
|
1642
|
-
-
|
|
1643
|
-
- 🛡️ **Reliability** - Circuit breaker pattern for resilience
|
|
1644
|
-
- 👮 **Strict Mode** - Enforced controller patterns
|
|
1645
|
-
- ⚠️ **Standardized Errors** - Consistent 4xx/5xx error formats
|
|
1639
|
+
- 🚧 **Framework Plugins** - Drop-in adapters for [Express](https://expressjs.com/), [Koa](https://koajs.com/), and [Elysia](https://elysiajs.com/)
|
|
1640
|
+
- 🚧 **Enhanced WebSockets** - Event support and HTTP simulation
|
|
1641
|
+
- 🚧 **Benchmarks** - Comprehensive performance comparisons
|
|
1642
|
+
- 🚧 **Scaling** - Automatic clustering support
|
|
1643
|
+
- 🚧 **RPC Support** - [tRPC](https://trpc.io/) and [gRPC](https://grpc.io/) integration
|
|
1644
|
+
- 🚧 **Binary Formats** - [Protobuf](https://protobuf.dev/) and [MessagePack](https://msgpack.org/) support
|
|
1645
|
+
- 🚧 **Reliability** - Circuit breaker pattern for resilience
|
|
1646
|
+
- 🚧 **Standardized Errors** - Consistent 4xx/5xx error formats
|
|
1646
1647
|
|
|
1647
1648
|
## 🤝 Contributing
|
|
1648
1649
|
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route information extracted from AST
|
|
3
|
+
*/
|
|
4
|
+
export interface RouteInfo {
|
|
5
|
+
method: string;
|
|
6
|
+
path: string;
|
|
7
|
+
handlerName?: string;
|
|
8
|
+
handlerSource?: string;
|
|
9
|
+
requestTypes?: {
|
|
10
|
+
body?: any;
|
|
11
|
+
query?: Record<string, string>;
|
|
12
|
+
params?: Record<string, string>;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
};
|
|
15
|
+
responseType?: string;
|
|
16
|
+
responseSchema?: any;
|
|
17
|
+
summary?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
tags?: string[];
|
|
20
|
+
operationId?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Dependency information
|
|
24
|
+
*/
|
|
25
|
+
interface DependencyInfo {
|
|
26
|
+
packageName: string;
|
|
27
|
+
version?: string;
|
|
28
|
+
importPath: string;
|
|
29
|
+
isExternal: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Application/Router instance found in code
|
|
33
|
+
*/
|
|
34
|
+
export interface ApplicationInstance {
|
|
35
|
+
name: string;
|
|
36
|
+
filePath: string;
|
|
37
|
+
className: 'Shokupan' | 'ShokupanRouter' | 'Controller';
|
|
38
|
+
routes: RouteInfo[];
|
|
39
|
+
mounted: MountInfo[];
|
|
40
|
+
}
|
|
41
|
+
interface MountInfo {
|
|
42
|
+
prefix: string;
|
|
43
|
+
target: string;
|
|
44
|
+
dependency?: DependencyInfo;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Main analyzer class
|
|
48
|
+
*/
|
|
49
|
+
export declare class OpenAPIAnalyzer {
|
|
50
|
+
private rootDir;
|
|
51
|
+
private files;
|
|
52
|
+
private applications;
|
|
53
|
+
private program?;
|
|
54
|
+
private entrypoint?;
|
|
55
|
+
constructor(rootDir: string, entrypoint?: string);
|
|
56
|
+
/**
|
|
57
|
+
* Main analysis entry point
|
|
58
|
+
*/
|
|
59
|
+
/**
|
|
60
|
+
* Main analysis entry point
|
|
61
|
+
*/
|
|
62
|
+
analyze(): Promise<{
|
|
63
|
+
applications: ApplicationInstance[];
|
|
64
|
+
}>;
|
|
65
|
+
/**
|
|
66
|
+
* Recursively scan directory for TypeScript/JavaScript files
|
|
67
|
+
*/
|
|
68
|
+
private scanDirectory;
|
|
69
|
+
/**
|
|
70
|
+
* Process source maps to reconstruct TypeScript
|
|
71
|
+
*/
|
|
72
|
+
private processSourceMaps;
|
|
73
|
+
/**
|
|
74
|
+
* Parse TypeScript files and create AST
|
|
75
|
+
*/
|
|
76
|
+
private parseTypeScriptFiles;
|
|
77
|
+
/**
|
|
78
|
+
* Find all Shokupan/ShokupanRouter instances
|
|
79
|
+
*/
|
|
80
|
+
private findApplications;
|
|
81
|
+
/**
|
|
82
|
+
* Visit AST node to find application instances
|
|
83
|
+
*/
|
|
84
|
+
private visitNode;
|
|
85
|
+
/**
|
|
86
|
+
* Extract route information from applications
|
|
87
|
+
*/
|
|
88
|
+
private extractRoutes;
|
|
89
|
+
/**
|
|
90
|
+
* Extract routes from a Controller class
|
|
91
|
+
*/
|
|
92
|
+
private extractRoutesFromController;
|
|
93
|
+
/**
|
|
94
|
+
* Extract routes from a specific file
|
|
95
|
+
*/
|
|
96
|
+
private extractRoutesFromFile;
|
|
97
|
+
/**
|
|
98
|
+
* Extract route information from a route call (e.g., app.get('/path', handler))
|
|
99
|
+
*/
|
|
100
|
+
private extractRouteFromCall;
|
|
101
|
+
/**
|
|
102
|
+
* Analyze a route handler to extract type information
|
|
103
|
+
*/
|
|
104
|
+
private analyzeHandler;
|
|
105
|
+
/**
|
|
106
|
+
* Convert an Expression node to an OpenAPI schema (best effort)
|
|
107
|
+
*/
|
|
108
|
+
private convertExpressionToSchema;
|
|
109
|
+
/**
|
|
110
|
+
* Check if an expression is a call to ctx.body()
|
|
111
|
+
*/
|
|
112
|
+
private isCtxBodyCall;
|
|
113
|
+
/**
|
|
114
|
+
* Convert a TypeScript TypeNode to an OpenAPI schema
|
|
115
|
+
*/
|
|
116
|
+
private convertTypeNodeToSchema;
|
|
117
|
+
/**
|
|
118
|
+
* Extract mount information from mount call
|
|
119
|
+
*/
|
|
120
|
+
private extractMountFromCall;
|
|
121
|
+
/**
|
|
122
|
+
* Check if a reference is to an external dependency
|
|
123
|
+
*/
|
|
124
|
+
private checkIfExternalDependency;
|
|
125
|
+
/**
|
|
126
|
+
* Get package version from package.json
|
|
127
|
+
*/
|
|
128
|
+
private getPackageVersion;
|
|
129
|
+
/**
|
|
130
|
+
* Generate OpenAPI specification
|
|
131
|
+
*/
|
|
132
|
+
generateOpenAPISpec(): any;
|
|
133
|
+
/**
|
|
134
|
+
* Convert a type string to an OpenAPI schema
|
|
135
|
+
*/
|
|
136
|
+
private typeToSchema;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Analyze a directory and generate OpenAPI spec
|
|
140
|
+
*/
|
|
141
|
+
export declare function analyzeDirectory(directory: string): Promise<any>;
|
|
142
|
+
export {};
|
package/dist/cli.cjs
CHANGED
|
@@ -4,6 +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-BN0wFCML.cjs");
|
|
7
8
|
function _interopNamespaceDefault(e) {
|
|
8
9
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
9
10
|
if (e) {
|
|
@@ -71,7 +72,7 @@ export class ${name}Plugin extends ShokupanRouter {
|
|
|
71
72
|
}
|
|
72
73
|
`
|
|
73
74
|
};
|
|
74
|
-
async function
|
|
75
|
+
async function scaffold() {
|
|
75
76
|
console.clear();
|
|
76
77
|
p__namespace.intro(`Shokupan CLI Scaffolder`);
|
|
77
78
|
if (!fs.existsSync("package.json")) {
|
|
@@ -148,7 +149,66 @@ async function main() {
|
|
|
148
149
|
const nextSteps = ` -> ${finalPath}
|
|
149
150
|
Make sure to register it in your main application file if necessary.`;
|
|
150
151
|
p__namespace.note(nextSteps, "Next steps");
|
|
151
|
-
p__namespace.outro(`Problems? Open an issue at https://github.com/dotglitch/
|
|
152
|
+
p__namespace.outro(`Problems? Open an issue at https://github.com/dotglitch/shokupan`);
|
|
153
|
+
}
|
|
154
|
+
async function analyze() {
|
|
155
|
+
console.clear();
|
|
156
|
+
p__namespace.intro(`Shokupan OpenAPI Analyzer`);
|
|
157
|
+
const args = process.argv.slice(2);
|
|
158
|
+
let directory = process.cwd();
|
|
159
|
+
let outputPath = "openapi.json";
|
|
160
|
+
const analyzeIndex = args.indexOf("analyze");
|
|
161
|
+
if (analyzeIndex !== -1 && args.length > analyzeIndex + 1) {
|
|
162
|
+
const nextArg = args[analyzeIndex + 1];
|
|
163
|
+
if (!nextArg.startsWith("--")) {
|
|
164
|
+
directory = path.resolve(nextArg);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const outputIndex = args.indexOf("--output");
|
|
168
|
+
if (outputIndex !== -1 && args.length > outputIndex + 1) {
|
|
169
|
+
outputPath = args[outputIndex + 1];
|
|
170
|
+
}
|
|
171
|
+
if (!fs.existsSync(directory)) {
|
|
172
|
+
p__namespace.cancel(`Directory not found: ${directory}`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
const s = p__namespace.spinner();
|
|
176
|
+
s.start(`Analyzing directory: ${directory}`);
|
|
177
|
+
try {
|
|
178
|
+
const spec = await openapiAnalyzer.analyzeDirectory(directory);
|
|
179
|
+
s.stop("Analysis complete");
|
|
180
|
+
const fullOutputPath = path.resolve(outputPath);
|
|
181
|
+
fs.writeFileSync(fullOutputPath, JSON.stringify(spec, null, 2));
|
|
182
|
+
p__namespace.note(`OpenAPI spec written to: ${fullOutputPath}`, "Success");
|
|
183
|
+
const pathCount = Object.keys(spec.paths || {}).length;
|
|
184
|
+
p__namespace.note(`Found ${pathCount} unique paths`, "Summary");
|
|
185
|
+
p__namespace.outro("Done!");
|
|
186
|
+
} catch (error) {
|
|
187
|
+
s.stop("Analysis failed");
|
|
188
|
+
p__namespace.cancel(`Error: ${error.message}`);
|
|
189
|
+
console.error(error);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function main() {
|
|
194
|
+
const args = process.argv.slice(2);
|
|
195
|
+
const command = args[0];
|
|
196
|
+
if (command === "analyze") {
|
|
197
|
+
await analyze();
|
|
198
|
+
} else if (command === "scaffold" || !command) {
|
|
199
|
+
await scaffold();
|
|
200
|
+
} else {
|
|
201
|
+
console.log("Shokupan CLI");
|
|
202
|
+
console.log("");
|
|
203
|
+
console.log("Commands:");
|
|
204
|
+
console.log(" scaffold (default) - Scaffold controllers, middleware, or plugins");
|
|
205
|
+
console.log(" analyze <directory> - Analyze a Shokupan application and generate OpenAPI spec");
|
|
206
|
+
console.log("");
|
|
207
|
+
console.log("Usage:");
|
|
208
|
+
console.log(" shokupan scaffold");
|
|
209
|
+
console.log(" shokupan analyze <directory> [--output openapi.json]");
|
|
210
|
+
process.exit(0);
|
|
211
|
+
}
|
|
152
212
|
}
|
|
153
213
|
main().catch(console.error);
|
|
154
214
|
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.cjs","sources":["../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env bun\nimport * as p from '@clack/prompts';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { setTimeout } from 'node:timers/promises';\n\nconst templates = {\n controller: (name: string) => `import { Controller, Get, Ctx } from 'shokupan';\nimport { ShokupanContext } from 'shokupan';\n\n@Controller('/${name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}')\nexport class ${name}Controller {\n @Get('/')\n public index(@Ctx() ctx: ShokupanContext) {\n return { message: 'Hello from ${name}Controller' };\n }\n}\n`,\n middleware: (name: string) => `import { ShokupanContext, NextFn } from 'shokupan';\n\n/**\n * ${name} Middleware\n */\nexport const ${name}Middleware = async (ctx: ShokupanContext, next: NextFn) => {\n // Before next\n // console.log('${name} Middleware - Request');\n\n const result = await next();\n\n // After next\n // console.log('${name} Middleware - Response');\n \n return result;\n};\n`,\n plugin: (name: string) => `import { ShokupanRouter } from 'shokupan';\nimport { ShokupanContext } from 'shokupan';\n\nexport interface ${name}Options {\n // Define options here\n}\n\nexport class ${name}Plugin extends ShokupanRouter {\n constructor(private options: ${name}Options = {}) {\n super();\n this.init();\n }\n\n private init() {\n this.get('/', (ctx: ShokupanContext) => {\n return { message: '${name} Plugin Active' };\n });\n }\n}\n`\n};\n\nasync function main() {\n console.clear();\n p.intro(`Shokupan CLI Scaffolder`);\n\n // Check if running in a project root\n if (!fs.existsSync('package.json')) {\n p.note('Warning: No package.json found in current directory. Are you in the project root?');\n }\n\n const project = await p.group(\n {\n type: () => p.select({\n message: 'What do you want to scaffold?',\n options: [\n { value: 'controller', label: 'Controller' },\n { value: 'middleware', label: 'Middleware' },\n { value: 'plugin', label: 'Plugin' },\n ],\n }),\n name: () => p.text({\n message: 'Name (PascalCase, e.g. UserAuth):',\n validate: (value) => {\n if (!value) return 'Name is required';\n if (!/^[A-Z][a-zA-Z0-9]*$/.test(value)) return 'Please use PascalCase';\n return undefined;\n },\n }),\n dir: () => p.text({\n message: 'Output directory (leave empty for default):',\n placeholder: 'src/controllers',\n }),\n },\n {\n onCancel: () => {\n p.cancel('Operation cancelled.');\n process.exit(0);\n },\n }\n );\n\n const type = project.type as keyof typeof templates;\n const name = project.name;\n let dir = project.dir;\n\n if (!dir || dir.trim() === '') {\n switch (type) {\n case 'controller': dir = 'src/controllers'; break;\n case 'middleware': dir = 'src/middleware'; break;\n case 'plugin': dir = 'src/plugins'; break;\n }\n }\n\n // Convert PascalCase to kebab-case for filename\n const kebabName = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\n const fileName = `${kebabName}.ts`;\n\n const finalPath = path.join(process.cwd(), dir, fileName);\n\n // Ensure directory exists\n if (!fs.existsSync(path.dirname(finalPath))) {\n fs.mkdirSync(path.dirname(finalPath), { recursive: true });\n }\n\n // Check for overwrite\n if (fs.existsSync(finalPath)) {\n const overwrite = await p.confirm({\n message: `File ${finalPath} already exists. Overwrite?`,\n initialValue: false\n });\n\n if (p.isCancel(overwrite) || !overwrite) {\n p.cancel('Operation cancelled.');\n process.exit(0);\n }\n }\n\n const s = p.spinner();\n s.start(`Creating ${type}...`);\n\n await setTimeout(500); // Artificial delay to show spinner\n\n const content = templates[type](name);\n fs.writeFileSync(finalPath, content);\n\n s.stop(`Created ${type}`);\n\n const nextSteps = ` -> ${finalPath}\nMake sure to register it in your main application file if necessary.`;\n\n p.note(nextSteps, 'Next steps');\n\n p.outro(`Problems? Open an issue at https://github.com/dotglitch/express.ts`);\n}\n\nmain().catch(console.error);;\n"],"names":["p","setTimeout"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.cjs","sources":["../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env bun\nimport * as p from '@clack/prompts';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { setTimeout } from 'node:timers/promises';\nimport { analyzeDirectory } from '../analysis/openapi-analyzer';\n\nconst templates = {\n controller: (name: string) => `import { Controller, Get, Ctx } from 'shokupan';\nimport { ShokupanContext } from 'shokupan';\n\n@Controller('/${name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}')\nexport class ${name}Controller {\n @Get('/')\n public index(@Ctx() ctx: ShokupanContext) {\n return { message: 'Hello from ${name}Controller' };\n }\n}\n`,\n middleware: (name: string) => `import { ShokupanContext, NextFn } from 'shokupan';\n\n/**\n * ${name} Middleware\n */\nexport const ${name}Middleware = async (ctx: ShokupanContext, next: NextFn) => {\n // Before next\n // console.log('${name} Middleware - Request');\n\n const result = await next();\n\n // After next\n // console.log('${name} Middleware - Response');\n \n return result;\n};\n`,\n plugin: (name: string) => `import { ShokupanRouter } from 'shokupan';\nimport { ShokupanContext } from 'shokupan';\n\nexport interface ${name}Options {\n // Define options here\n}\n\nexport class ${name}Plugin extends ShokupanRouter {\n constructor(private options: ${name}Options = {}) {\n super();\n this.init();\n }\n\n private init() {\n this.get('/', (ctx: ShokupanContext) => {\n return { message: '${name} Plugin Active' };\n });\n }\n}\n`\n};\n\nasync function scaffold() {\n console.clear();\n p.intro(`Shokupan CLI Scaffolder`);\n\n // Check if running in a project root\n if (!fs.existsSync('package.json')) {\n p.note('Warning: No package.json found in current directory. Are you in the project root?');\n }\n\n const project = await p.group(\n {\n type: () => p.select({\n message: 'What do you want to scaffold?',\n options: [\n { value: 'controller', label: 'Controller' },\n { value: 'middleware', label: 'Middleware' },\n { value: 'plugin', label: 'Plugin' },\n ],\n }),\n name: () => p.text({\n message: 'Name (PascalCase, e.g. UserAuth):',\n validate: (value) => {\n if (!value) return 'Name is required';\n if (!/^[A-Z][a-zA-Z0-9]*$/.test(value)) return 'Please use PascalCase';\n return undefined;\n },\n }),\n dir: () => p.text({\n message: 'Output directory (leave empty for default):',\n placeholder: 'src/controllers',\n }),\n },\n {\n onCancel: () => {\n p.cancel('Operation cancelled.');\n process.exit(0);\n },\n }\n );\n\n const type = project.type as keyof typeof templates;\n const name = project.name;\n let dir = project.dir;\n\n if (!dir || dir.trim() === '') {\n switch (type) {\n case 'controller': dir = 'src/controllers'; break;\n case 'middleware': dir = 'src/middleware'; break;\n case 'plugin': dir = 'src/plugins'; break;\n }\n }\n\n // Convert PascalCase to kebab-case for filename\n const kebabName = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\n const fileName = `${kebabName}.ts`;\n\n const finalPath = path.join(process.cwd(), dir, fileName);\n\n // Ensure directory exists\n if (!fs.existsSync(path.dirname(finalPath))) {\n fs.mkdirSync(path.dirname(finalPath), { recursive: true });\n }\n\n // Check for overwrite\n if (fs.existsSync(finalPath)) {\n const overwrite = await p.confirm({\n message: `File ${finalPath} already exists. Overwrite?`,\n initialValue: false\n });\n\n if (p.isCancel(overwrite) || !overwrite) {\n p.cancel('Operation cancelled.');\n process.exit(0);\n }\n }\n\n const s = p.spinner();\n s.start(`Creating ${type}...`);\n\n await setTimeout(500); // Artificial delay to show spinner\n\n const content = templates[type](name);\n fs.writeFileSync(finalPath, content);\n\n s.stop(`Created ${type}`);\n\n const nextSteps = ` -> ${finalPath}\nMake sure to register it in your main application file if necessary.`;\n\n p.note(nextSteps, 'Next steps');\n\n p.outro(`Problems? Open an issue at https://github.com/dotglitch/shokupan`);\n}\n\nasync function analyze() {\n console.clear();\n p.intro(`Shokupan OpenAPI Analyzer`);\n\n const args = process.argv.slice(2);\n let directory = process.cwd();\n let outputPath = 'openapi.json';\n\n // Parse command line arguments\n // analyze [directory] [--output file.json]\n const analyzeIndex = args.indexOf('analyze');\n if (analyzeIndex !== -1 && args.length > analyzeIndex + 1) {\n const nextArg = args[analyzeIndex + 1];\n if (!nextArg.startsWith('--')) {\n directory = path.resolve(nextArg);\n }\n }\n\n const outputIndex = args.indexOf('--output');\n if (outputIndex !== -1 && args.length > outputIndex + 1) {\n outputPath = args[outputIndex + 1];\n }\n\n // Verify directory exists\n if (!fs.existsSync(directory)) {\n p.cancel(`Directory not found: ${directory}`);\n process.exit(1);\n }\n\n const s = p.spinner();\n s.start(`Analyzing directory: ${directory}`);\n\n try {\n const spec = await analyzeDirectory(directory);\n\n s.stop('Analysis complete');\n\n // Write to file\n const fullOutputPath = path.resolve(outputPath);\n fs.writeFileSync(fullOutputPath, JSON.stringify(spec, null, 2));\n\n p.note(`OpenAPI spec written to: ${fullOutputPath}`, 'Success');\n\n // Show summary\n const pathCount = Object.keys(spec.paths || {}).length;\n p.note(`Found ${pathCount} unique paths`, 'Summary');\n\n p.outro('Done!');\n } catch (error: any) {\n s.stop('Analysis failed');\n p.cancel(`Error: ${error.message}`);\n console.error(error);\n process.exit(1);\n }\n}\n\nasync function main() {\n const args = process.argv.slice(2);\n const command = args[0];\n\n if (command === 'analyze') {\n await analyze();\n } else if (command === 'scaffold' || !command) {\n // Default to scaffold for backwards compatibility\n await scaffold();\n } else {\n console.log('Shokupan CLI');\n console.log('');\n console.log('Commands:');\n console.log(' scaffold (default) - Scaffold controllers, middleware, or plugins');\n console.log(' analyze <directory> - Analyze a Shokupan application and generate OpenAPI spec');\n console.log('');\n console.log('Usage:');\n console.log(' shokupan scaffold');\n console.log(' shokupan analyze <directory> [--output openapi.json]');\n process.exit(0);\n }\n}\n\nmain().catch(console.error);\n"],"names":["p","setTimeout","analyzeDirectory"],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -3,6 +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-BTExMLX4.js";
|
|
6
7
|
const templates = {
|
|
7
8
|
controller: (name) => `import { Controller, Get, Ctx } from 'shokupan';
|
|
8
9
|
import { ShokupanContext } from 'shokupan';
|
|
@@ -53,7 +54,7 @@ export class ${name}Plugin extends ShokupanRouter {
|
|
|
53
54
|
}
|
|
54
55
|
`
|
|
55
56
|
};
|
|
56
|
-
async function
|
|
57
|
+
async function scaffold() {
|
|
57
58
|
console.clear();
|
|
58
59
|
p.intro(`Shokupan CLI Scaffolder`);
|
|
59
60
|
if (!fs.existsSync("package.json")) {
|
|
@@ -130,7 +131,66 @@ async function main() {
|
|
|
130
131
|
const nextSteps = ` -> ${finalPath}
|
|
131
132
|
Make sure to register it in your main application file if necessary.`;
|
|
132
133
|
p.note(nextSteps, "Next steps");
|
|
133
|
-
p.outro(`Problems? Open an issue at https://github.com/dotglitch/
|
|
134
|
+
p.outro(`Problems? Open an issue at https://github.com/dotglitch/shokupan`);
|
|
135
|
+
}
|
|
136
|
+
async function analyze() {
|
|
137
|
+
console.clear();
|
|
138
|
+
p.intro(`Shokupan OpenAPI Analyzer`);
|
|
139
|
+
const args = process.argv.slice(2);
|
|
140
|
+
let directory = process.cwd();
|
|
141
|
+
let outputPath = "openapi.json";
|
|
142
|
+
const analyzeIndex = args.indexOf("analyze");
|
|
143
|
+
if (analyzeIndex !== -1 && args.length > analyzeIndex + 1) {
|
|
144
|
+
const nextArg = args[analyzeIndex + 1];
|
|
145
|
+
if (!nextArg.startsWith("--")) {
|
|
146
|
+
directory = path.resolve(nextArg);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const outputIndex = args.indexOf("--output");
|
|
150
|
+
if (outputIndex !== -1 && args.length > outputIndex + 1) {
|
|
151
|
+
outputPath = args[outputIndex + 1];
|
|
152
|
+
}
|
|
153
|
+
if (!fs.existsSync(directory)) {
|
|
154
|
+
p.cancel(`Directory not found: ${directory}`);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
const s = p.spinner();
|
|
158
|
+
s.start(`Analyzing directory: ${directory}`);
|
|
159
|
+
try {
|
|
160
|
+
const spec = await analyzeDirectory(directory);
|
|
161
|
+
s.stop("Analysis complete");
|
|
162
|
+
const fullOutputPath = path.resolve(outputPath);
|
|
163
|
+
fs.writeFileSync(fullOutputPath, JSON.stringify(spec, null, 2));
|
|
164
|
+
p.note(`OpenAPI spec written to: ${fullOutputPath}`, "Success");
|
|
165
|
+
const pathCount = Object.keys(spec.paths || {}).length;
|
|
166
|
+
p.note(`Found ${pathCount} unique paths`, "Summary");
|
|
167
|
+
p.outro("Done!");
|
|
168
|
+
} catch (error) {
|
|
169
|
+
s.stop("Analysis failed");
|
|
170
|
+
p.cancel(`Error: ${error.message}`);
|
|
171
|
+
console.error(error);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async function main() {
|
|
176
|
+
const args = process.argv.slice(2);
|
|
177
|
+
const command = args[0];
|
|
178
|
+
if (command === "analyze") {
|
|
179
|
+
await analyze();
|
|
180
|
+
} else if (command === "scaffold" || !command) {
|
|
181
|
+
await scaffold();
|
|
182
|
+
} else {
|
|
183
|
+
console.log("Shokupan CLI");
|
|
184
|
+
console.log("");
|
|
185
|
+
console.log("Commands:");
|
|
186
|
+
console.log(" scaffold (default) - Scaffold controllers, middleware, or plugins");
|
|
187
|
+
console.log(" analyze <directory> - Analyze a Shokupan application and generate OpenAPI spec");
|
|
188
|
+
console.log("");
|
|
189
|
+
console.log("Usage:");
|
|
190
|
+
console.log(" shokupan scaffold");
|
|
191
|
+
console.log(" shokupan analyze <directory> [--output openapi.json]");
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
134
194
|
}
|
|
135
195
|
main().catch(console.error);
|
|
136
196
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env bun\nimport * as p from '@clack/prompts';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { setTimeout } from 'node:timers/promises';\n\nconst templates = {\n controller: (name: string) => `import { Controller, Get, Ctx } from 'shokupan';\nimport { ShokupanContext } from 'shokupan';\n\n@Controller('/${name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}')\nexport class ${name}Controller {\n @Get('/')\n public index(@Ctx() ctx: ShokupanContext) {\n return { message: 'Hello from ${name}Controller' };\n }\n}\n`,\n middleware: (name: string) => `import { ShokupanContext, NextFn } from 'shokupan';\n\n/**\n * ${name} Middleware\n */\nexport const ${name}Middleware = async (ctx: ShokupanContext, next: NextFn) => {\n // Before next\n // console.log('${name} Middleware - Request');\n\n const result = await next();\n\n // After next\n // console.log('${name} Middleware - Response');\n \n return result;\n};\n`,\n plugin: (name: string) => `import { ShokupanRouter } from 'shokupan';\nimport { ShokupanContext } from 'shokupan';\n\nexport interface ${name}Options {\n // Define options here\n}\n\nexport class ${name}Plugin extends ShokupanRouter {\n constructor(private options: ${name}Options = {}) {\n super();\n this.init();\n }\n\n private init() {\n this.get('/', (ctx: ShokupanContext) => {\n return { message: '${name} Plugin Active' };\n });\n }\n}\n`\n};\n\nasync function main() {\n console.clear();\n p.intro(`Shokupan CLI Scaffolder`);\n\n // Check if running in a project root\n if (!fs.existsSync('package.json')) {\n p.note('Warning: No package.json found in current directory. Are you in the project root?');\n }\n\n const project = await p.group(\n {\n type: () => p.select({\n message: 'What do you want to scaffold?',\n options: [\n { value: 'controller', label: 'Controller' },\n { value: 'middleware', label: 'Middleware' },\n { value: 'plugin', label: 'Plugin' },\n ],\n }),\n name: () => p.text({\n message: 'Name (PascalCase, e.g. UserAuth):',\n validate: (value) => {\n if (!value) return 'Name is required';\n if (!/^[A-Z][a-zA-Z0-9]*$/.test(value)) return 'Please use PascalCase';\n return undefined;\n },\n }),\n dir: () => p.text({\n message: 'Output directory (leave empty for default):',\n placeholder: 'src/controllers',\n }),\n },\n {\n onCancel: () => {\n p.cancel('Operation cancelled.');\n process.exit(0);\n },\n }\n );\n\n const type = project.type as keyof typeof templates;\n const name = project.name;\n let dir = project.dir;\n\n if (!dir || dir.trim() === '') {\n switch (type) {\n case 'controller': dir = 'src/controllers'; break;\n case 'middleware': dir = 'src/middleware'; break;\n case 'plugin': dir = 'src/plugins'; break;\n }\n }\n\n // Convert PascalCase to kebab-case for filename\n const kebabName = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\n const fileName = `${kebabName}.ts`;\n\n const finalPath = path.join(process.cwd(), dir, fileName);\n\n // Ensure directory exists\n if (!fs.existsSync(path.dirname(finalPath))) {\n fs.mkdirSync(path.dirname(finalPath), { recursive: true });\n }\n\n // Check for overwrite\n if (fs.existsSync(finalPath)) {\n const overwrite = await p.confirm({\n message: `File ${finalPath} already exists. Overwrite?`,\n initialValue: false\n });\n\n if (p.isCancel(overwrite) || !overwrite) {\n p.cancel('Operation cancelled.');\n process.exit(0);\n }\n }\n\n const s = p.spinner();\n s.start(`Creating ${type}...`);\n\n await setTimeout(500); // Artificial delay to show spinner\n\n const content = templates[type](name);\n fs.writeFileSync(finalPath, content);\n\n s.stop(`Created ${type}`);\n\n const nextSteps = ` -> ${finalPath}\nMake sure to register it in your main application file if necessary.`;\n\n p.note(nextSteps, 'Next steps');\n\n p.outro(`Problems? Open an issue at https://github.com/dotglitch/express.ts`);\n}\n\nmain().catch(console.error);;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env bun\nimport * as p from '@clack/prompts';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { setTimeout } from 'node:timers/promises';\nimport { analyzeDirectory } from '../analysis/openapi-analyzer';\n\nconst templates = {\n controller: (name: string) => `import { Controller, Get, Ctx } from 'shokupan';\nimport { ShokupanContext } from 'shokupan';\n\n@Controller('/${name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()}')\nexport class ${name}Controller {\n @Get('/')\n public index(@Ctx() ctx: ShokupanContext) {\n return { message: 'Hello from ${name}Controller' };\n }\n}\n`,\n middleware: (name: string) => `import { ShokupanContext, NextFn } from 'shokupan';\n\n/**\n * ${name} Middleware\n */\nexport const ${name}Middleware = async (ctx: ShokupanContext, next: NextFn) => {\n // Before next\n // console.log('${name} Middleware - Request');\n\n const result = await next();\n\n // After next\n // console.log('${name} Middleware - Response');\n \n return result;\n};\n`,\n plugin: (name: string) => `import { ShokupanRouter } from 'shokupan';\nimport { ShokupanContext } from 'shokupan';\n\nexport interface ${name}Options {\n // Define options here\n}\n\nexport class ${name}Plugin extends ShokupanRouter {\n constructor(private options: ${name}Options = {}) {\n super();\n this.init();\n }\n\n private init() {\n this.get('/', (ctx: ShokupanContext) => {\n return { message: '${name} Plugin Active' };\n });\n }\n}\n`\n};\n\nasync function scaffold() {\n console.clear();\n p.intro(`Shokupan CLI Scaffolder`);\n\n // Check if running in a project root\n if (!fs.existsSync('package.json')) {\n p.note('Warning: No package.json found in current directory. Are you in the project root?');\n }\n\n const project = await p.group(\n {\n type: () => p.select({\n message: 'What do you want to scaffold?',\n options: [\n { value: 'controller', label: 'Controller' },\n { value: 'middleware', label: 'Middleware' },\n { value: 'plugin', label: 'Plugin' },\n ],\n }),\n name: () => p.text({\n message: 'Name (PascalCase, e.g. UserAuth):',\n validate: (value) => {\n if (!value) return 'Name is required';\n if (!/^[A-Z][a-zA-Z0-9]*$/.test(value)) return 'Please use PascalCase';\n return undefined;\n },\n }),\n dir: () => p.text({\n message: 'Output directory (leave empty for default):',\n placeholder: 'src/controllers',\n }),\n },\n {\n onCancel: () => {\n p.cancel('Operation cancelled.');\n process.exit(0);\n },\n }\n );\n\n const type = project.type as keyof typeof templates;\n const name = project.name;\n let dir = project.dir;\n\n if (!dir || dir.trim() === '') {\n switch (type) {\n case 'controller': dir = 'src/controllers'; break;\n case 'middleware': dir = 'src/middleware'; break;\n case 'plugin': dir = 'src/plugins'; break;\n }\n }\n\n // Convert PascalCase to kebab-case for filename\n const kebabName = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\n const fileName = `${kebabName}.ts`;\n\n const finalPath = path.join(process.cwd(), dir, fileName);\n\n // Ensure directory exists\n if (!fs.existsSync(path.dirname(finalPath))) {\n fs.mkdirSync(path.dirname(finalPath), { recursive: true });\n }\n\n // Check for overwrite\n if (fs.existsSync(finalPath)) {\n const overwrite = await p.confirm({\n message: `File ${finalPath} already exists. Overwrite?`,\n initialValue: false\n });\n\n if (p.isCancel(overwrite) || !overwrite) {\n p.cancel('Operation cancelled.');\n process.exit(0);\n }\n }\n\n const s = p.spinner();\n s.start(`Creating ${type}...`);\n\n await setTimeout(500); // Artificial delay to show spinner\n\n const content = templates[type](name);\n fs.writeFileSync(finalPath, content);\n\n s.stop(`Created ${type}`);\n\n const nextSteps = ` -> ${finalPath}\nMake sure to register it in your main application file if necessary.`;\n\n p.note(nextSteps, 'Next steps');\n\n p.outro(`Problems? Open an issue at https://github.com/dotglitch/shokupan`);\n}\n\nasync function analyze() {\n console.clear();\n p.intro(`Shokupan OpenAPI Analyzer`);\n\n const args = process.argv.slice(2);\n let directory = process.cwd();\n let outputPath = 'openapi.json';\n\n // Parse command line arguments\n // analyze [directory] [--output file.json]\n const analyzeIndex = args.indexOf('analyze');\n if (analyzeIndex !== -1 && args.length > analyzeIndex + 1) {\n const nextArg = args[analyzeIndex + 1];\n if (!nextArg.startsWith('--')) {\n directory = path.resolve(nextArg);\n }\n }\n\n const outputIndex = args.indexOf('--output');\n if (outputIndex !== -1 && args.length > outputIndex + 1) {\n outputPath = args[outputIndex + 1];\n }\n\n // Verify directory exists\n if (!fs.existsSync(directory)) {\n p.cancel(`Directory not found: ${directory}`);\n process.exit(1);\n }\n\n const s = p.spinner();\n s.start(`Analyzing directory: ${directory}`);\n\n try {\n const spec = await analyzeDirectory(directory);\n\n s.stop('Analysis complete');\n\n // Write to file\n const fullOutputPath = path.resolve(outputPath);\n fs.writeFileSync(fullOutputPath, JSON.stringify(spec, null, 2));\n\n p.note(`OpenAPI spec written to: ${fullOutputPath}`, 'Success');\n\n // Show summary\n const pathCount = Object.keys(spec.paths || {}).length;\n p.note(`Found ${pathCount} unique paths`, 'Summary');\n\n p.outro('Done!');\n } catch (error: any) {\n s.stop('Analysis failed');\n p.cancel(`Error: ${error.message}`);\n console.error(error);\n process.exit(1);\n }\n}\n\nasync function main() {\n const args = process.argv.slice(2);\n const command = args[0];\n\n if (command === 'analyze') {\n await analyze();\n } else if (command === 'scaffold' || !command) {\n // Default to scaffold for backwards compatibility\n await scaffold();\n } else {\n console.log('Shokupan CLI');\n console.log('');\n console.log('Commands:');\n console.log(' scaffold (default) - Scaffold controllers, middleware, or plugins');\n console.log(' analyze <directory> - Analyze a Shokupan application and generate OpenAPI spec');\n console.log('');\n console.log('Usage:');\n console.log(' shokupan scaffold');\n console.log(' shokupan analyze <directory> [--output openapi.json]');\n process.exit(0);\n }\n}\n\nmain().catch(console.error);\n"],"names":[],"mappings":"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"}
|
package/dist/context.d.ts
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import { BodyInit } from 'bun';
|
|
1
|
+
import { BodyInit, Server } from 'bun';
|
|
2
2
|
import { ShokupanRequest } from './request';
|
|
3
3
|
import { ShokupanResponse } from './response';
|
|
4
|
-
import {
|
|
4
|
+
import { Shokupan } from './shokupan';
|
|
5
|
+
import { CookieOptions, JSXRenderer } from './types';
|
|
5
6
|
type HeadersInit = Headers | Record<string, string> | [string, string][];
|
|
7
|
+
export interface HandlerStackItem {
|
|
8
|
+
name: string;
|
|
9
|
+
file: string;
|
|
10
|
+
line: number;
|
|
11
|
+
stateChanges?: Record<string, any>;
|
|
12
|
+
}
|
|
6
13
|
export declare class ShokupanContext<State extends Record<string, any> = Record<string, any>> {
|
|
7
14
|
readonly request: ShokupanRequest<any>;
|
|
8
|
-
readonly
|
|
15
|
+
readonly server?: Server;
|
|
16
|
+
readonly app?: Shokupan;
|
|
17
|
+
private _url;
|
|
9
18
|
params: Record<string, string>;
|
|
10
19
|
state: State;
|
|
20
|
+
handlerStack: HandlerStackItem[];
|
|
11
21
|
readonly response: ShokupanResponse;
|
|
12
|
-
|
|
22
|
+
_finalResponse?: Response;
|
|
23
|
+
constructor(request: ShokupanRequest<any>, server?: Server, state?: State, app?: Shokupan, enableMiddlewareTracking?: boolean);
|
|
24
|
+
get url(): URL;
|
|
13
25
|
/**
|
|
14
26
|
* Base request
|
|
15
27
|
*/
|
|
@@ -21,23 +33,54 @@ export declare class ShokupanContext<State extends Record<string, any> = Record<
|
|
|
21
33
|
/**
|
|
22
34
|
* Request path
|
|
23
35
|
*/
|
|
24
|
-
get path():
|
|
36
|
+
get path(): any;
|
|
25
37
|
/**
|
|
26
38
|
* Request query params
|
|
27
39
|
*/
|
|
28
40
|
get query(): {
|
|
29
41
|
[k: string]: string;
|
|
30
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Client IP address
|
|
45
|
+
*/
|
|
46
|
+
get ip(): Bun.SocketAddress;
|
|
47
|
+
/**
|
|
48
|
+
* Request hostname (e.g. "localhost")
|
|
49
|
+
*/
|
|
50
|
+
get hostname(): string;
|
|
51
|
+
/**
|
|
52
|
+
* Request host (e.g. "localhost:3000")
|
|
53
|
+
*/
|
|
54
|
+
get host(): string;
|
|
55
|
+
/**
|
|
56
|
+
* Request protocol (e.g. "http:", "https:")
|
|
57
|
+
*/
|
|
58
|
+
get protocol(): string;
|
|
59
|
+
/**
|
|
60
|
+
* Whether request is secure (https)
|
|
61
|
+
*/
|
|
62
|
+
get secure(): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Request origin (e.g. "http://localhost:3000")
|
|
65
|
+
*/
|
|
66
|
+
get origin(): string;
|
|
31
67
|
/**
|
|
32
68
|
* Request headers
|
|
33
69
|
*/
|
|
34
70
|
get headers(): any;
|
|
71
|
+
/**
|
|
72
|
+
* Get a request header
|
|
73
|
+
* @param name Header name
|
|
74
|
+
*/
|
|
75
|
+
get(name: string): any;
|
|
35
76
|
/**
|
|
36
77
|
* Base response object
|
|
37
78
|
*/
|
|
38
79
|
get res(): ShokupanResponse;
|
|
39
80
|
/**
|
|
40
81
|
* Helper to set a header on the response
|
|
82
|
+
* @param key Header key
|
|
83
|
+
* @param value Header value
|
|
41
84
|
*/
|
|
42
85
|
set(key: string, value: string): this;
|
|
43
86
|
/**
|
|
@@ -84,5 +127,16 @@ export declare class ShokupanContext<State extends Record<string, any> = Record<
|
|
|
84
127
|
* Respond with a file
|
|
85
128
|
*/
|
|
86
129
|
file(path: string, fileOptions?: BlobPropertyBag, responseOptions?: ResponseInit): Response;
|
|
130
|
+
/**
|
|
131
|
+
* JSX Rendering Function
|
|
132
|
+
*/
|
|
133
|
+
renderer?: JSXRenderer;
|
|
134
|
+
/**
|
|
135
|
+
* Render a JSX element
|
|
136
|
+
* @param element JSX Element
|
|
137
|
+
* @param status HTTP Status
|
|
138
|
+
* @param headers HTTP Headers
|
|
139
|
+
*/
|
|
140
|
+
jsx(element: any, args?: Parameters<JSXRenderer>[1], status?: number, headers?: HeadersInit): Promise<Response>;
|
|
87
141
|
}
|
|
88
142
|
export {};
|
package/dist/decorators.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Middleware } from './types';
|
|
1
|
+
import { GuardAPISpec, MethodAPISpec, Middleware } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Class Decorator: Defines the base path for a controller.
|
|
4
4
|
*/
|
|
@@ -13,6 +13,10 @@ export declare const Query: (name?: string) => (target: any, propertyKey: string
|
|
|
13
13
|
export declare const Headers: (name?: string) => (target: any, propertyKey: string, parameterIndex: number) => void;
|
|
14
14
|
export declare const Req: (name?: string) => (target: any, propertyKey: string, parameterIndex: number) => void;
|
|
15
15
|
export declare const Ctx: (name?: string) => (target: any, propertyKey: string, parameterIndex: number) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Decorator: Overrides the OpenAPI specification for a route.
|
|
18
|
+
*/
|
|
19
|
+
export declare function Spec(spec: MethodAPISpec | GuardAPISpec): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
|
|
16
20
|
export declare const Get: (path?: string) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
|
|
17
21
|
export declare const Post: (path?: string) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
|
|
18
22
|
export declare const Put: (path?: string) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
|