shokupan 0.9.0 → 0.10.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.
Files changed (49) hide show
  1. package/dist/analyzer-BqIe1p0R.js +35 -0
  2. package/dist/analyzer-BqIe1p0R.js.map +1 -0
  3. package/dist/analyzer-CKLGLFtx.cjs +35 -0
  4. package/dist/analyzer-CKLGLFtx.cjs.map +1 -0
  5. package/dist/{analyzer-Ce_7JxZh.js → analyzer.impl-CV6W1Eq7.js} +238 -21
  6. package/dist/analyzer.impl-CV6W1Eq7.js.map +1 -0
  7. package/dist/{analyzer-Bei1sVWp.cjs → analyzer.impl-D9Yi1Hax.cjs} +237 -20
  8. package/dist/analyzer.impl-D9Yi1Hax.cjs.map +1 -0
  9. package/dist/cli.cjs +1 -1
  10. package/dist/cli.js +1 -1
  11. package/dist/context.d.ts +19 -7
  12. package/dist/http-server-BEMPIs33.cjs.map +1 -1
  13. package/dist/http-server-CCeagTyU.js.map +1 -1
  14. package/dist/index.cjs +1500 -275
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.js +1482 -256
  18. package/dist/index.js.map +1 -1
  19. package/dist/plugins/application/api-explorer/plugin.d.ts +9 -0
  20. package/dist/plugins/application/api-explorer/static/explorer-client.mjs +880 -0
  21. package/dist/plugins/application/api-explorer/static/style.css +767 -0
  22. package/dist/plugins/application/api-explorer/static/theme.css +128 -0
  23. package/dist/plugins/application/asyncapi/generator.d.ts +3 -0
  24. package/dist/plugins/application/asyncapi/plugin.d.ts +15 -0
  25. package/dist/plugins/application/asyncapi/static/asyncapi-client.mjs +748 -0
  26. package/dist/plugins/application/asyncapi/static/style.css +565 -0
  27. package/dist/plugins/application/asyncapi/static/theme.css +128 -0
  28. package/dist/plugins/application/auth.d.ts +3 -1
  29. package/dist/plugins/application/dashboard/metrics-collector.d.ts +3 -1
  30. package/dist/plugins/application/dashboard/plugin.d.ts +13 -3
  31. package/dist/plugins/application/dashboard/static/registry.css +0 -53
  32. package/dist/plugins/application/dashboard/static/styles.css +29 -20
  33. package/dist/plugins/application/dashboard/static/tabulator.css +83 -31
  34. package/dist/plugins/application/dashboard/static/theme.css +128 -0
  35. package/dist/plugins/application/graphql-apollo.d.ts +33 -0
  36. package/dist/plugins/application/graphql-yoga.d.ts +25 -0
  37. package/dist/plugins/application/openapi/analyzer.d.ts +12 -119
  38. package/dist/plugins/application/openapi/analyzer.impl.d.ts +167 -0
  39. package/dist/plugins/application/scalar.d.ts +9 -2
  40. package/dist/router.d.ts +80 -51
  41. package/dist/shokupan.d.ts +14 -8
  42. package/dist/util/datastore.d.ts +71 -7
  43. package/dist/util/decorators.d.ts +2 -2
  44. package/dist/util/types.d.ts +96 -3
  45. package/package.json +32 -12
  46. package/dist/analyzer-Bei1sVWp.cjs.map +0 -1
  47. package/dist/analyzer-Ce_7JxZh.js.map +0 -1
  48. package/dist/plugins/application/dashboard/static/scrollbar.css +0 -24
  49. package/dist/plugins/application/dashboard/template.eta +0 -246
@@ -0,0 +1,167 @@
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
+ emits?: {
22
+ event: string;
23
+ payload?: any;
24
+ location?: {
25
+ startLine: number;
26
+ endLine: number;
27
+ };
28
+ }[];
29
+ sourceContext?: {
30
+ file: string;
31
+ startLine: number;
32
+ endLine: number;
33
+ highlights?: {
34
+ startLine: number;
35
+ endLine: number;
36
+ type: 'emit' | 'return-success' | 'return-warning';
37
+ }[];
38
+ };
39
+ }
40
+ /**
41
+ * Dependency information
42
+ */
43
+ interface DependencyInfo {
44
+ packageName: string;
45
+ version?: string;
46
+ importPath: string;
47
+ isExternal: boolean;
48
+ }
49
+ /**
50
+ * Application/Router instance found in code
51
+ */
52
+ export interface ApplicationInstance {
53
+ name: string;
54
+ filePath: string;
55
+ className: 'Shokupan' | 'ShokupanRouter' | 'Controller';
56
+ controllerPrefix?: string;
57
+ routes: RouteInfo[];
58
+ mounted: MountInfo[];
59
+ }
60
+ interface MountInfo {
61
+ prefix: string;
62
+ target: string;
63
+ targetFilePath?: string;
64
+ dependency?: DependencyInfo;
65
+ sourceContext?: {
66
+ file: string;
67
+ startLine: number;
68
+ endLine: number;
69
+ };
70
+ }
71
+ /**
72
+ * Main analyzer class
73
+ */
74
+ export declare class OpenAPIAnalyzer {
75
+ private rootDir;
76
+ private files;
77
+ private applications;
78
+ private program?;
79
+ private entrypoint?;
80
+ constructor(rootDir: string, entrypoint?: string);
81
+ /**
82
+ * Main analysis entry point
83
+ */
84
+ /**
85
+ * Main analysis entry point
86
+ */
87
+ analyze(): Promise<{
88
+ applications: ApplicationInstance[];
89
+ }>;
90
+ /**
91
+ * Remove GenericModules that are not mounted by any Shokupan application/router
92
+ */
93
+ private pruneApplications;
94
+ /**
95
+ * Recursively scan directory for TypeScript/JavaScript files
96
+ */
97
+ private scanDirectory;
98
+ /**
99
+ * Process source maps to reconstruct TypeScript
100
+ */
101
+ private processSourceMaps;
102
+ /**
103
+ * Parse TypeScript files and create AST
104
+ */
105
+ private parseTypeScriptFiles;
106
+ /**
107
+ * Find all Shokupan/ShokupanRouter instances
108
+ */
109
+ private findApplications;
110
+ /**
111
+ * Visit AST node to find application instances
112
+ */
113
+ private visitNode;
114
+ /**
115
+ * Extract route information from applications
116
+ */
117
+ private extractRoutes;
118
+ /**
119
+ * Extract routes from a Controller class
120
+ */
121
+ private extractRoutesFromController;
122
+ /**
123
+ * Extract routes from a specific file
124
+ */
125
+ private extractRoutesFromFile;
126
+ /**
127
+ * Extract route information from a route call (e.g., app.get('/path', handler))
128
+ */
129
+ private extractRouteFromCall;
130
+ /**
131
+ * Analyze a route handler to extract type information
132
+ */
133
+ private analyzeHandler;
134
+ /**
135
+ * Convert an Expression node to an OpenAPI schema (best effort)
136
+ */
137
+ private convertExpressionToSchema;
138
+ /**
139
+ * Check if an expression is a call to ctx.body()
140
+ */
141
+ private isCtxBodyCall;
142
+ /**
143
+ * Convert a TypeScript TypeNode to an OpenAPI schema
144
+ */
145
+ private convertTypeNodeToSchema;
146
+ /**
147
+ * Extract mount information from mount call
148
+ */
149
+ private extractMountFromCall;
150
+ /**
151
+ * Check if a reference is to an external dependency
152
+ */
153
+ private checkIfExternalDependency;
154
+ /**
155
+ * Get package version from package.json
156
+ */
157
+ private getPackageVersion;
158
+ /**
159
+ * Generate OpenAPI specification
160
+ */
161
+ generateOpenAPISpec(): any;
162
+ }
163
+ /**
164
+ * Analyze a directory and generate OpenAPI spec
165
+ */
166
+ export declare function analyzeDirectory(directory: string): Promise<any>;
167
+ export {};
@@ -21,6 +21,11 @@ export type ScalarPluginOptions = {
21
21
  * Only works with TypeScript entrypoints.
22
22
  */
23
23
  enableStaticAnalysis?: boolean;
24
+ /**
25
+ * Path to mount the plugin to.
26
+ * @default '/reference'
27
+ */
28
+ path?: string;
24
29
  };
25
30
  /**
26
31
  * Scalar plugin. This plugin provides an API reference interface for your API.
@@ -29,8 +34,10 @@ export type ScalarPluginOptions = {
29
34
  */
30
35
  export declare class ScalarPlugin extends ShokupanRouter<any> implements ShokupanPlugin {
31
36
  private readonly pluginOptions;
37
+ private eta;
32
38
  constructor(pluginOptions?: ScalarPluginOptions);
33
- onInit(app: Shokupan, options?: ShokupanPluginOptions): void;
34
- private init;
39
+ onInit(app: Shokupan, options?: ShokupanPluginOptions): Promise<void>;
40
+ private ensureEta;
41
+ private initRoutes;
35
42
  onMount(parent: ShokupanRouter<any>): void;
36
43
  }
package/dist/router.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ShokupanContext } from './context';
2
2
  import { Shokupan } from './shokupan';
3
+ import { SurrealDatastore } from './util/datastore';
3
4
  import { $appRoot, $childControllers, $childRouters, $isApplication, $isMounted, $isRouter, $mountPath, $parent, $routes } from './util/symbol';
4
5
  import { GuardAPISpec, HeadersInit, JSXRenderer, Method, MethodAPISpec, Middleware, OpenAPIOptions, ProcessResult, RequestOptions, RouteMetadata, RouteParams, ShokupanController, ShokupanHandler, ShokupanHooks, ShokupanRoute, ShokupanRouteConfig, StaticServeOptions } from './util/types';
5
6
  export declare const RouterRegistry: Map<string, ShokupanRouter<any>>;
@@ -73,73 +74,92 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
73
74
  private [$parent];
74
75
  [$childRouters]: ShokupanRouter<T>[];
75
76
  [$childControllers]: ShokupanController[];
77
+ get db(): SurrealDatastore | undefined;
76
78
  private hookCache;
77
79
  private hooksInitialized;
78
80
  middleware: Middleware[];
79
- get rootConfig(): {
80
- [x: string]: any;
81
- port?: number;
82
- hostname?: string;
83
- development?: boolean;
84
- enableAsyncLocalStorage?: boolean;
85
- enableOpenApiGen?: boolean;
86
- reusePort?: boolean;
87
- controllersOnly?: boolean;
81
+ get rootConfig(): Partial<{
82
+ [key: string]: any;
83
+ port: number;
84
+ hostname: string;
85
+ development: boolean;
86
+ enableAsyncLocalStorage: boolean;
87
+ enableOpenApiGen: boolean;
88
+ enableAsyncApiGen: boolean;
89
+ reusePort: boolean;
90
+ controllersOnly: boolean;
88
91
  enableTracing?: boolean;
89
92
  jsonParser?: "native" | "parse-json" | "secure-json-parse";
90
93
  autoBackpressureFeedback?: boolean;
91
94
  autoBackpressureLevel?: number;
92
- enableMiddlewareTracking?: boolean;
95
+ enableMiddlewareTracking: boolean;
93
96
  middlewareTrackingMaxCapacity?: number;
94
97
  enableHTTPBridge?: boolean;
95
98
  websocketErrorHandler?: (err: any, ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
96
99
  idGenerator?: () => string;
97
100
  middlewareTrackingTTL?: number;
98
- httpLogger?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void;
99
- logger?: {
100
- verbose?: boolean;
101
- info?: (msg: string, props: Record<string, any>) => void;
102
- debug?: (msg: string, props: Record<string, any>) => void;
103
- warning?: (msg: string, props: Record<string, any>) => void;
104
- error?: (msg: string, props: Record<string, any>) => void;
105
- fatal?: (msg: string, props: Record<string, any>) => void;
101
+ httpLogger: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void;
102
+ logger: {
103
+ verbose: boolean;
104
+ info: (msg: string, props: Record<string, any>) => void;
105
+ debug: (msg: string, props: Record<string, any>) => void;
106
+ warning: (msg: string, props: Record<string, any>) => void;
107
+ error: (msg: string, props: Record<string, any>) => void;
108
+ fatal: (msg: string, props: Record<string, any>) => void;
106
109
  };
107
- readTimeout?: number;
108
- requestTimeout?: number;
109
- writeTimeout?: number;
110
- renderer?: JSXRenderer;
111
- serverFactory?: import('.').ServerFactory;
112
- hooks?: {
113
- onError?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, error: unknown) => void | Promise<void>;
114
- onRequestStart?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
115
- onRequestEnd?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
116
- onResponseStart?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, response: Response) => void | Promise<void>;
117
- onResponseEnd?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, response: Response) => void | Promise<void>;
118
- beforeValidate?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, data: any) => void | Promise<void>;
119
- afterValidate?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, data: any) => void | Promise<void>;
120
- onReadTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
121
- onWriteTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
122
- onRequestTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
123
- } | {
124
- onError?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, error: unknown) => void | Promise<void>;
125
- onRequestStart?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
126
- onRequestEnd?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
127
- onResponseStart?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, response: Response) => void | Promise<void>;
128
- onResponseEnd?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, response: Response) => void | Promise<void>;
129
- beforeValidate?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, data: any) => void | Promise<void>;
130
- afterValidate?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>, data: any) => void | Promise<void>;
131
- onReadTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
132
- onWriteTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
133
- onRequestTimeout?: (ctx: ShokupanContext<Record<string, any>, Record<string, string>>) => void | Promise<void>;
134
- }[];
135
- validateStatusCodes?: boolean;
136
- };
110
+ readTimeout: number;
111
+ requestTimeout: number;
112
+ writeTimeout: number;
113
+ renderer: JSXRenderer;
114
+ serverFactory: import('.').ServerFactory;
115
+ hooks: ShokupanHooks<Record<string, any>> | ShokupanHooks<Record<string, any>>[];
116
+ validateStatusCodes: boolean;
117
+ surreal?: {
118
+ engines?: import('surrealdb').Engines;
119
+ url?: string;
120
+ connectOptions?: import('surrealdb').ConnectOptions;
121
+ namespace?: string;
122
+ database?: string;
123
+ };
124
+ aiPlugin?: {
125
+ enabled?: boolean;
126
+ name_for_human?: string;
127
+ name_for_model?: string;
128
+ description_for_human?: string;
129
+ description_for_model?: string;
130
+ auth?: {
131
+ type: "none" | "service_http" | "user_http" | "oauth";
132
+ [key: string]: any;
133
+ };
134
+ api?: {
135
+ type: "openapi";
136
+ url?: string;
137
+ is_user_authenticated?: boolean;
138
+ };
139
+ logo_url?: string;
140
+ contact_email?: string;
141
+ legal_info_url?: string;
142
+ };
143
+ apiCatalog?: {
144
+ enabled?: boolean;
145
+ versions?: Array<{
146
+ name: string;
147
+ url: string;
148
+ spec_url: string;
149
+ }>;
150
+ };
151
+ }>;
137
152
  get root(): Shokupan<any>;
138
153
  [$routes]: ShokupanRoute[];
139
154
  private trie;
140
155
  metadata?: RouteMetadata;
141
156
  private currentGuards;
142
157
  private eventHandlers;
158
+ /**
159
+ * Registers middleware for this router.
160
+ * Middleware will run for all routes matched by this router.
161
+ */
162
+ use(middleware: Middleware): this;
143
163
  getComponentRegistry(): {
144
164
  metadata: RouteMetadata;
145
165
  middleware: {
@@ -186,6 +206,10 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
186
206
  * Finds an event handler(s) by name.
187
207
  */
188
208
  findEvent(name: string): ShokupanHandler<T>[] | null;
209
+ /**
210
+ * Returns all registered event handlers.
211
+ */
212
+ getEventHandlers(): Map<string, ShokupanHandler<T>[]>;
189
213
  /**
190
214
  * Mounts a controller instance to a path prefix.
191
215
  *
@@ -251,7 +275,7 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
251
275
  * @param arg.renderer - JSX renderer for the route
252
276
  * @param arg.controller - Controller for the route
253
277
  */
254
- add({ method, path, spec, handler, regex: customRegex, group, requestTimeout, renderer, controller }: {
278
+ add({ method, path, spec, handler, regex: customRegex, group, requestTimeout, renderer, controller, metadata, middleware }: {
255
279
  method: Method;
256
280
  path: string;
257
281
  spec?: MethodAPISpec;
@@ -261,6 +285,11 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
261
285
  requestTimeout?: number;
262
286
  renderer?: JSXRenderer;
263
287
  controller?: any;
288
+ metadata?: {
289
+ file: string;
290
+ line: number;
291
+ };
292
+ middleware?: Middleware[];
264
293
  }): this;
265
294
  /**
266
295
  * Adds a GET route to the router.
@@ -397,7 +426,7 @@ export declare class ShokupanRouter<T extends Record<string, any> = Record<strin
397
426
  * Generates an OpenAPI 3.1 Document by recursing through the router and its descendants.
398
427
  * Now includes runtime analysis of handler functions to infer request/response types.
399
428
  */
400
- generateApiSpec(options?: OpenAPIOptions): any;
429
+ generateApiSpec(options?: OpenAPIOptions): Promise<any>;
401
430
  private ensureHooksInitialized;
402
- runHooks(name: keyof ShokupanHooks, ...args: any[]): Promise<void>;
431
+ runHooks(name: keyof ShokupanHooks, ...args: any[]): void | Promise<void[]>;
403
432
  }
@@ -2,6 +2,7 @@ import { $dispatch } from './util/symbol';
2
2
  import { Middleware, ProcessResult, RequestOptions, ShokupanConfig, ShokupanPlugin } from './util/types';
3
3
  import { Server } from 'bun';
4
4
  import { ShokupanRouter } from './router';
5
+ import { SurrealDatastore } from './util/datastore';
5
6
  import { ShokupanRequest } from './util/request';
6
7
  /**
7
8
  * Shokupan Application
@@ -70,18 +71,23 @@ import { ShokupanRequest } from './util/request';
70
71
  export declare class Shokupan<T = any> extends ShokupanRouter<T> {
71
72
  readonly applicationConfig: ShokupanConfig;
72
73
  openApiSpec?: any;
74
+ asyncApiSpec?: any;
73
75
  private composedMiddleware?;
74
76
  private cpuMonitor?;
75
77
  private server?;
78
+ private datastore?;
79
+ dbPromise?: Promise<any>;
80
+ get db(): SurrealDatastore | undefined;
76
81
  get logger(): {
77
- verbose?: boolean;
78
- info?: (msg: string, props: Record<string, any>) => void;
79
- debug?: (msg: string, props: Record<string, any>) => void;
80
- warning?: (msg: string, props: Record<string, any>) => void;
81
- error?: (msg: string, props: Record<string, any>) => void;
82
- fatal?: (msg: string, props: Record<string, any>) => void;
82
+ verbose: boolean;
83
+ info: (msg: string, props: Record<string, any>) => void;
84
+ debug: (msg: string, props: Record<string, any>) => void;
85
+ warning: (msg: string, props: Record<string, any>) => void;
86
+ error: (msg: string, props: Record<string, any>) => void;
87
+ fatal: (msg: string, props: Record<string, any>) => void;
83
88
  };
84
89
  constructor(applicationConfig?: ShokupanConfig);
90
+ private initDatastore;
85
91
  /**
86
92
  * Adds middleware to the application.
87
93
  */
@@ -109,7 +115,7 @@ export declare class Shokupan<T = any> extends ShokupanRouter<T> {
109
115
  * @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.
110
116
  * @returns The server instance.
111
117
  */
112
- listen(port?: number): Promise<Server>;
118
+ listen(port?: number): Promise<Server<any>>;
113
119
  /**
114
120
  * Stops the application server.
115
121
  *
@@ -142,6 +148,6 @@ export declare class Shokupan<T = any> extends ShokupanRouter<T> {
142
148
  * @param server - The server instance.
143
149
  * @returns The response to send.
144
150
  */
145
- fetch(req: Request, server?: Server): Promise<Response>;
151
+ fetch(req: Request, server?: Server<any>): Promise<Response>;
146
152
  private handleRequest;
147
153
  }
@@ -1,7 +1,71 @@
1
- import { RecordId, Range, Table } from 'surrealdb';
2
- export declare const datastore: {
3
- get<T extends Record<string, any>>(recordId: RecordId | Table | Range<any, any>): Promise<T>;
4
- set(recordId: RecordId, value: Record<string, any>): Promise<any>;
5
- query<T extends Record<string, any>>(query: string, vars?: Record<string, unknown>): Promise<T>;
6
- readonly ready: Promise<any>;
7
- };
1
+ import { RecordId, Surreal, RecordIdRange, Table } from 'surrealdb';
2
+ export declare class SurrealDatastore {
3
+ private readonly db;
4
+ constructor(db: Surreal);
5
+ createSchema(): void;
6
+ /**
7
+ * Select a record or contents of a table by its ID.
8
+ */
9
+ select<T = unknown>(id: RecordId | RecordIdRange | Table): Promise<(T extends object ? T extends {
10
+ id: infer Id;
11
+ } ? Id extends RecordId<string, import('surrealdb').RecordIdValue> ? T : Id extends import('surrealdb').RecordIdValue ? {
12
+ id: RecordId<string, Id>;
13
+ } & Omit<T, "id"> : {
14
+ id: RecordId;
15
+ } & Omit<T, "id"> : {
16
+ id: RecordId;
17
+ } & T : {
18
+ id: RecordId;
19
+ }) extends infer T_1 ? { [K in keyof T_1]: T_1[K]; } : never>;
20
+ /**
21
+ * Merge update data into a record by its ID.
22
+ */
23
+ merge<T extends Record<string, any>>(id: RecordId, data: T): Promise<(T extends object ? T extends {
24
+ id: infer Id;
25
+ } ? Id extends RecordId<string, import('surrealdb').RecordIdValue> ? T : Id extends import('surrealdb').RecordIdValue ? {
26
+ id: RecordId<string, Id>;
27
+ } & Omit<T, "id"> : {
28
+ id: RecordId;
29
+ } & Omit<T, "id"> : {
30
+ id: RecordId;
31
+ } & T : {
32
+ id: RecordId;
33
+ }) extends infer T_1 ? { [K in keyof T_1]: T_1[K]; } : never>;
34
+ /**
35
+ * Create a record by its ID.
36
+ */
37
+ create<T extends Record<string, any>>(id: RecordId, data: Omit<T, 'id'>): Promise<{
38
+ id: RecordId;
39
+ }>;
40
+ /**
41
+ * Upsert a record by its ID.
42
+ */
43
+ upsert<T extends Record<string, any>>(id: RecordId, data: T): Promise<(T extends object ? T extends {
44
+ id: infer Id;
45
+ } ? Id extends RecordId<string, import('surrealdb').RecordIdValue> ? T : Id extends import('surrealdb').RecordIdValue ? {
46
+ id: RecordId<string, Id>;
47
+ } & Omit<T, "id"> : {
48
+ id: RecordId;
49
+ } & Omit<T, "id"> : {
50
+ id: RecordId;
51
+ } & T : {
52
+ id: RecordId;
53
+ }) extends infer T_1 ? { [K in keyof T_1]: T_1[K]; } : never>;
54
+ /**
55
+ * Delete a record by its ID.
56
+ */
57
+ delete(id: RecordId): Promise<{
58
+ id: RecordId;
59
+ }>;
60
+ /**
61
+ * Run a SurrealDB query.
62
+ */
63
+ query<T extends Array<unknown>>(query: string, vars?: Record<string, any>): Promise<T extends [] ? unknown[] : { [K in keyof T]: T[K]; }>;
64
+ /**
65
+ * Create a relationship between two records.
66
+ */
67
+ relate(fromId: any, edgeId: any, toId: any, data?: Record<string, any>): Promise<{
68
+ [x: string]: /*elided*/ any;
69
+ }>;
70
+ disconnect(): Promise<true>;
71
+ }
@@ -1,5 +1,5 @@
1
1
  import { RateLimitOptions } from '../plugins/middleware/rate-limit';
2
- import { GuardAPISpec, MethodAPISpec, Middleware } from './types';
2
+ import { AsyncAPISpec, GuardAPISpec, MethodAPISpec, Middleware } from './types';
3
3
  /**
4
4
  * Class Decorator: Defines the base path for a controller.
5
5
  */
@@ -35,7 +35,7 @@ export declare const Ctx: (name?: string) => (target: any, propertyKey: string,
35
35
  /**
36
36
  * Decorator: Overrides the OpenAPI specification for a route.
37
37
  */
38
- export declare function Spec(spec: MethodAPISpec | GuardAPISpec): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
38
+ export declare function Spec(spec: MethodAPISpec | GuardAPISpec | AsyncAPISpec): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
39
39
  /**
40
40
  * Decorator: Binds a method to the GET HTTP verb.
41
41
  */
@@ -1,6 +1,7 @@
1
1
  import { OpenAPI } from '@scalar/openapi-types';
2
2
  import { Server } from 'bun';
3
3
  import { Server as NodeServer } from 'node:http';
4
+ import { ConnectOptions, Engines } from 'surrealdb';
4
5
  import { ShokupanContext } from '../context';
5
6
  import { $isRouter } from './symbol';
6
7
  export type HeadersInit = Headers | Record<string, string> | [string, string][];
@@ -61,6 +62,27 @@ export interface OpenAPIOptions {
61
62
  defaultTagGroup?: string;
62
63
  defaultTag?: string;
63
64
  }
65
+ export interface AsyncAPIOptions {
66
+ info?: {
67
+ title: string;
68
+ version: string;
69
+ description?: string;
70
+ };
71
+ defaultTag?: string;
72
+ }
73
+ export interface AsyncAPISpec {
74
+ type?: 'publish' | 'subscribe';
75
+ summary?: string;
76
+ description?: string;
77
+ tags?: string[];
78
+ message?: {
79
+ name?: string;
80
+ title?: string;
81
+ summary?: string;
82
+ payload?: any;
83
+ headers?: any;
84
+ };
85
+ }
64
86
  export interface ShokupanHooks<T = any> {
65
87
  onError?: (ctx: ShokupanContext<T>, error: unknown) => void | Promise<void>;
66
88
  onRequestStart?: (ctx: ShokupanContext<T>) => void | Promise<void>;
@@ -97,7 +119,7 @@ export declare enum RouteParamType {
97
119
  CONTEXT = "CONTEXT"
98
120
  }
99
121
  export interface ServerFactory {
100
- (options: any): Server | Promise<Server> | NodeServer | Promise<NodeServer>;
122
+ (options: any): Server<any> | Promise<Server<any>> | NodeServer | Promise<NodeServer>;
101
123
  }
102
124
  export type NextFn = () => Promise<any>;
103
125
  export type Middleware = ((ctx: ShokupanContext<unknown>, next: NextFn) => Promise<any> | any) & {
@@ -167,7 +189,7 @@ export type ShokupanRoute = {
167
189
  /**
168
190
  * OpenAPI spec for the route
169
191
  */
170
- handlerSpec?: MethodAPISpec;
192
+ handlerSpec?: MethodAPISpec | AsyncAPISpec;
171
193
  /**
172
194
  * Group for the route
173
195
  */
@@ -209,8 +231,12 @@ export type ShokupanRoute = {
209
231
  * Controller instance this route belongs to
210
232
  */
211
233
  controller?: any;
234
+ /**
235
+ * Middleware stack metadata for this route (Controller/Method level)
236
+ */
237
+ middleware?: Middleware[];
212
238
  };
213
- export type ShokupanConfig<T extends Record<string, any> = Record<string, any>> = DeepPartial<{
239
+ export type ShokupanConfig<T extends Record<string, any> = Record<string, any>> = Partial<{
214
240
  /**
215
241
  * The port to be used for the server.
216
242
  * @default 3000
@@ -237,6 +263,11 @@ export type ShokupanConfig<T extends Record<string, any> = Record<string, any>>
237
263
  * @default true
238
264
  */
239
265
  enableOpenApiGen: boolean;
266
+ /**
267
+ * Whether to enable AsyncAPI generation.
268
+ * @default false
269
+ */
270
+ enableAsyncApiGen: boolean;
240
271
  /**
241
272
  * Whether to reuse the port.
242
273
  * @default false
@@ -379,6 +410,68 @@ export type ShokupanConfig<T extends Record<string, any> = Record<string, any>>
379
410
  * @default true
380
411
  */
381
412
  validateStatusCodes: boolean;
413
+ /**
414
+ * Configuration for SurrealDB.
415
+ */
416
+ surreal?: {
417
+ /**
418
+ * SurrealDB engines.
419
+ * @default Embedded
420
+ */
421
+ engines?: Engines;
422
+ /**
423
+ * SurrealDB connection URL.
424
+ * @default 'rocksdb://database'
425
+ */
426
+ url?: string;
427
+ /**
428
+ * SurrealDB connection options.
429
+ */
430
+ connectOptions?: ConnectOptions;
431
+ /**
432
+ * SurrealDB namespace.
433
+ */
434
+ namespace?: string;
435
+ /**
436
+ * SurrealDB database.
437
+ */
438
+ database?: string;
439
+ };
440
+ /**
441
+ * Configuration for the AI Plugin manifest (.well-known/ai-plugin.json).
442
+ * If enabled, Shokupan will serve the manifest at the standard location.
443
+ */
444
+ aiPlugin?: {
445
+ enabled?: boolean;
446
+ name_for_human?: string;
447
+ name_for_model?: string;
448
+ description_for_human?: string;
449
+ description_for_model?: string;
450
+ auth?: {
451
+ type: 'none' | 'service_http' | 'user_http' | 'oauth';
452
+ [key: string]: any;
453
+ };
454
+ api?: {
455
+ type: 'openapi';
456
+ url?: string;
457
+ is_user_authenticated?: boolean;
458
+ };
459
+ logo_url?: string;
460
+ contact_email?: string;
461
+ legal_info_url?: string;
462
+ };
463
+ /**
464
+ * Configuration for the API Catalog (.well-known/api-catalog).
465
+ * If enabled, Shokupan will serve the catalog at the standard location.
466
+ */
467
+ apiCatalog?: {
468
+ enabled?: boolean;
469
+ versions?: Array<{
470
+ name: string;
471
+ url: string;
472
+ spec_url: string;
473
+ }>;
474
+ };
382
475
  /**
383
476
  * Any other config options are allowed, but will be ignored.
384
477
  * @deprecated