@lantos1618/better-ui 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/openapi/index.d.mts +33 -24
- package/dist/openapi/index.d.ts +33 -24
- package/dist/openapi/index.js +81 -2
- package/dist/openapi/index.mjs +79 -1
- package/package.json +1 -1
package/dist/openapi/index.d.mts
CHANGED
|
@@ -1,30 +1,8 @@
|
|
|
1
|
-
import { T as Tool } from '../tool-yZTixiN2.mjs';
|
|
1
|
+
import { T as Tool, g as ToolContext } from '../tool-yZTixiN2.mjs';
|
|
2
2
|
import { J as JsonSchema } from '../schema-DdZf6066.mjs';
|
|
3
3
|
import 'zod';
|
|
4
4
|
import 'react';
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* OpenAPI 3.1 spec generator for Better UI tools.
|
|
8
|
-
*
|
|
9
|
-
* Converts a tool registry into a valid OpenAPI document.
|
|
10
|
-
* Each tool becomes a POST endpoint at `/api/tools/{toolName}`.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* import { generateOpenAPISpec } from '@lantos1618/better-ui/openapi';
|
|
15
|
-
* import { tools } from './tools';
|
|
16
|
-
*
|
|
17
|
-
* const spec = generateOpenAPISpec({
|
|
18
|
-
* title: 'My AI Tools API',
|
|
19
|
-
* version: '1.0.0',
|
|
20
|
-
* tools,
|
|
21
|
-
* });
|
|
22
|
-
*
|
|
23
|
-
* // Serve as JSON
|
|
24
|
-
* app.get('/openapi.json', (req, res) => res.json(spec));
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
6
|
interface OpenAPISpecConfig {
|
|
29
7
|
/** API title */
|
|
30
8
|
title: string;
|
|
@@ -109,5 +87,36 @@ declare function generateOpenAPISpec(config: OpenAPISpecConfig): OpenAPISpec;
|
|
|
109
87
|
* ```
|
|
110
88
|
*/
|
|
111
89
|
declare function openAPIHandler(config: OpenAPISpecConfig): (req: Request) => Response;
|
|
90
|
+
interface ToolRouterConfig {
|
|
91
|
+
/** Tool registry */
|
|
92
|
+
tools: Record<string, Tool>;
|
|
93
|
+
/** Base path prefix (default: '/api/tools') */
|
|
94
|
+
basePath?: string;
|
|
95
|
+
/** Optional context passed to every tool execution */
|
|
96
|
+
context?: Partial<ToolContext>;
|
|
97
|
+
/** Called before tool execution — throw to reject */
|
|
98
|
+
onBeforeExecute?: (toolName: string, input: unknown, req: Request) => Promise<void> | void;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Create a request handler that routes to individual tool endpoints.
|
|
102
|
+
* Each tool is callable at `POST {basePath}/{toolName}`.
|
|
103
|
+
*
|
|
104
|
+
* Also serves the OpenAPI spec at `GET {basePath}` and a Swagger UI at `GET {basePath}/docs`.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* // Next.js catch-all: app/api/tools/[...path]/route.ts
|
|
109
|
+
* import { toolRouter } from '@lantos1618/better-ui/openapi';
|
|
110
|
+
* const router = toolRouter({ tools });
|
|
111
|
+
* export const GET = router;
|
|
112
|
+
* export const POST = router;
|
|
113
|
+
*
|
|
114
|
+
* // Now callable:
|
|
115
|
+
* // POST /api/tools/weather { "city": "Tokyo" } -> { "result": { "temp": 22, ... } }
|
|
116
|
+
* // GET /api/tools -> OpenAPI JSON spec
|
|
117
|
+
* // GET /api/tools/docs -> Swagger UI
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
declare function toolRouter(config: ToolRouterConfig): (req: Request) => Promise<Response>;
|
|
112
121
|
|
|
113
|
-
export { type OpenAPISpec, type OpenAPISpecConfig, generateOpenAPISpec, openAPIHandler };
|
|
122
|
+
export { type OpenAPISpec, type OpenAPISpecConfig, type ToolRouterConfig, generateOpenAPISpec, openAPIHandler, toolRouter };
|
package/dist/openapi/index.d.ts
CHANGED
|
@@ -1,30 +1,8 @@
|
|
|
1
|
-
import { T as Tool } from '../tool-yZTixiN2.js';
|
|
1
|
+
import { T as Tool, g as ToolContext } from '../tool-yZTixiN2.js';
|
|
2
2
|
import { J as JsonSchema } from '../schema-DdZf6066.js';
|
|
3
3
|
import 'zod';
|
|
4
4
|
import 'react';
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* OpenAPI 3.1 spec generator for Better UI tools.
|
|
8
|
-
*
|
|
9
|
-
* Converts a tool registry into a valid OpenAPI document.
|
|
10
|
-
* Each tool becomes a POST endpoint at `/api/tools/{toolName}`.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* import { generateOpenAPISpec } from '@lantos1618/better-ui/openapi';
|
|
15
|
-
* import { tools } from './tools';
|
|
16
|
-
*
|
|
17
|
-
* const spec = generateOpenAPISpec({
|
|
18
|
-
* title: 'My AI Tools API',
|
|
19
|
-
* version: '1.0.0',
|
|
20
|
-
* tools,
|
|
21
|
-
* });
|
|
22
|
-
*
|
|
23
|
-
* // Serve as JSON
|
|
24
|
-
* app.get('/openapi.json', (req, res) => res.json(spec));
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
6
|
interface OpenAPISpecConfig {
|
|
29
7
|
/** API title */
|
|
30
8
|
title: string;
|
|
@@ -109,5 +87,36 @@ declare function generateOpenAPISpec(config: OpenAPISpecConfig): OpenAPISpec;
|
|
|
109
87
|
* ```
|
|
110
88
|
*/
|
|
111
89
|
declare function openAPIHandler(config: OpenAPISpecConfig): (req: Request) => Response;
|
|
90
|
+
interface ToolRouterConfig {
|
|
91
|
+
/** Tool registry */
|
|
92
|
+
tools: Record<string, Tool>;
|
|
93
|
+
/** Base path prefix (default: '/api/tools') */
|
|
94
|
+
basePath?: string;
|
|
95
|
+
/** Optional context passed to every tool execution */
|
|
96
|
+
context?: Partial<ToolContext>;
|
|
97
|
+
/** Called before tool execution — throw to reject */
|
|
98
|
+
onBeforeExecute?: (toolName: string, input: unknown, req: Request) => Promise<void> | void;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Create a request handler that routes to individual tool endpoints.
|
|
102
|
+
* Each tool is callable at `POST {basePath}/{toolName}`.
|
|
103
|
+
*
|
|
104
|
+
* Also serves the OpenAPI spec at `GET {basePath}` and a Swagger UI at `GET {basePath}/docs`.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* // Next.js catch-all: app/api/tools/[...path]/route.ts
|
|
109
|
+
* import { toolRouter } from '@lantos1618/better-ui/openapi';
|
|
110
|
+
* const router = toolRouter({ tools });
|
|
111
|
+
* export const GET = router;
|
|
112
|
+
* export const POST = router;
|
|
113
|
+
*
|
|
114
|
+
* // Now callable:
|
|
115
|
+
* // POST /api/tools/weather { "city": "Tokyo" } -> { "result": { "temp": 22, ... } }
|
|
116
|
+
* // GET /api/tools -> OpenAPI JSON spec
|
|
117
|
+
* // GET /api/tools/docs -> Swagger UI
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
declare function toolRouter(config: ToolRouterConfig): (req: Request) => Promise<Response>;
|
|
112
121
|
|
|
113
|
-
export { type OpenAPISpec, type OpenAPISpecConfig, generateOpenAPISpec, openAPIHandler };
|
|
122
|
+
export { type OpenAPISpec, type OpenAPISpecConfig, type ToolRouterConfig, generateOpenAPISpec, openAPIHandler, toolRouter };
|
package/dist/openapi/index.js
CHANGED
|
@@ -21,7 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var openapi_exports = {};
|
|
22
22
|
__export(openapi_exports, {
|
|
23
23
|
generateOpenAPISpec: () => generateOpenAPISpec,
|
|
24
|
-
openAPIHandler: () => openAPIHandler
|
|
24
|
+
openAPIHandler: () => openAPIHandler,
|
|
25
|
+
toolRouter: () => toolRouter
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(openapi_exports);
|
|
27
28
|
|
|
@@ -253,8 +254,86 @@ function openAPIHandler(config) {
|
|
|
253
254
|
}
|
|
254
255
|
});
|
|
255
256
|
}
|
|
257
|
+
function toolRouter(config) {
|
|
258
|
+
const basePath = config.basePath ?? "/api/tools";
|
|
259
|
+
return async (req) => {
|
|
260
|
+
const url = new URL(req.url);
|
|
261
|
+
const path = url.pathname;
|
|
262
|
+
if (req.method === "OPTIONS") {
|
|
263
|
+
return new Response(null, {
|
|
264
|
+
headers: {
|
|
265
|
+
"Access-Control-Allow-Origin": "*",
|
|
266
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
267
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
if (req.method === "GET" && (path === basePath || path === basePath + "/")) {
|
|
272
|
+
const spec = generateOpenAPISpec({
|
|
273
|
+
title: "Tool API",
|
|
274
|
+
version: "1.0.0",
|
|
275
|
+
tools: config.tools,
|
|
276
|
+
basePath,
|
|
277
|
+
serverUrl: url.origin
|
|
278
|
+
});
|
|
279
|
+
return new Response(JSON.stringify(spec, null, 2), {
|
|
280
|
+
headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" }
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
if (req.method === "GET" && (path === basePath + "/docs" || path === basePath + "/docs/")) {
|
|
284
|
+
const html = `<!DOCTYPE html><html><head><meta charset="utf-8"><title>API Docs</title>
|
|
285
|
+
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
|
|
286
|
+
</head><body><div id="swagger-ui"></div>
|
|
287
|
+
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
|
|
288
|
+
<script>SwaggerUIBundle({url:'${basePath}',dom_id:'#swagger-ui',deepLinking:true});</script>
|
|
289
|
+
</body></html>`;
|
|
290
|
+
return new Response(html, {
|
|
291
|
+
headers: { "Content-Type": "text/html", "Access-Control-Allow-Origin": "*" }
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if (req.method === "POST" && path.startsWith(basePath + "/")) {
|
|
295
|
+
const toolName = path.slice(basePath.length + 1).replace(/\/$/, "");
|
|
296
|
+
if (!toolName) {
|
|
297
|
+
return Response.json({ error: "Missing tool name" }, { status: 400 });
|
|
298
|
+
}
|
|
299
|
+
if (!Object.prototype.hasOwnProperty.call(config.tools, toolName)) {
|
|
300
|
+
return Response.json({ error: "Tool not found" }, { status: 404 });
|
|
301
|
+
}
|
|
302
|
+
const tool = config.tools[toolName];
|
|
303
|
+
let input;
|
|
304
|
+
try {
|
|
305
|
+
input = await req.json();
|
|
306
|
+
} catch {
|
|
307
|
+
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
if (config.onBeforeExecute) {
|
|
311
|
+
await config.onBeforeExecute(toolName, input, req);
|
|
312
|
+
}
|
|
313
|
+
const result = await tool.run(input, {
|
|
314
|
+
isServer: true,
|
|
315
|
+
headers: req.headers,
|
|
316
|
+
...config.context
|
|
317
|
+
});
|
|
318
|
+
return Response.json({ result }, {
|
|
319
|
+
headers: { "Access-Control-Allow-Origin": "*" }
|
|
320
|
+
});
|
|
321
|
+
} catch (error) {
|
|
322
|
+
if (error instanceof Error && error.name === "ZodError") {
|
|
323
|
+
return Response.json({ error: error.message }, { status: 400 });
|
|
324
|
+
}
|
|
325
|
+
return Response.json(
|
|
326
|
+
{ error: error instanceof Error ? error.message : "Tool execution failed" },
|
|
327
|
+
{ status: 500 }
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return Response.json({ error: "Not found" }, { status: 404 });
|
|
332
|
+
};
|
|
333
|
+
}
|
|
256
334
|
// Annotate the CommonJS export names for ESM import in node:
|
|
257
335
|
0 && (module.exports = {
|
|
258
336
|
generateOpenAPISpec,
|
|
259
|
-
openAPIHandler
|
|
337
|
+
openAPIHandler,
|
|
338
|
+
toolRouter
|
|
260
339
|
});
|
package/dist/openapi/index.mjs
CHANGED
|
@@ -91,7 +91,85 @@ function openAPIHandler(config) {
|
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
+
function toolRouter(config) {
|
|
95
|
+
const basePath = config.basePath ?? "/api/tools";
|
|
96
|
+
return async (req) => {
|
|
97
|
+
const url = new URL(req.url);
|
|
98
|
+
const path = url.pathname;
|
|
99
|
+
if (req.method === "OPTIONS") {
|
|
100
|
+
return new Response(null, {
|
|
101
|
+
headers: {
|
|
102
|
+
"Access-Control-Allow-Origin": "*",
|
|
103
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
104
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (req.method === "GET" && (path === basePath || path === basePath + "/")) {
|
|
109
|
+
const spec = generateOpenAPISpec({
|
|
110
|
+
title: "Tool API",
|
|
111
|
+
version: "1.0.0",
|
|
112
|
+
tools: config.tools,
|
|
113
|
+
basePath,
|
|
114
|
+
serverUrl: url.origin
|
|
115
|
+
});
|
|
116
|
+
return new Response(JSON.stringify(spec, null, 2), {
|
|
117
|
+
headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" }
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (req.method === "GET" && (path === basePath + "/docs" || path === basePath + "/docs/")) {
|
|
121
|
+
const html = `<!DOCTYPE html><html><head><meta charset="utf-8"><title>API Docs</title>
|
|
122
|
+
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
|
|
123
|
+
</head><body><div id="swagger-ui"></div>
|
|
124
|
+
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
|
|
125
|
+
<script>SwaggerUIBundle({url:'${basePath}',dom_id:'#swagger-ui',deepLinking:true});</script>
|
|
126
|
+
</body></html>`;
|
|
127
|
+
return new Response(html, {
|
|
128
|
+
headers: { "Content-Type": "text/html", "Access-Control-Allow-Origin": "*" }
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (req.method === "POST" && path.startsWith(basePath + "/")) {
|
|
132
|
+
const toolName = path.slice(basePath.length + 1).replace(/\/$/, "");
|
|
133
|
+
if (!toolName) {
|
|
134
|
+
return Response.json({ error: "Missing tool name" }, { status: 400 });
|
|
135
|
+
}
|
|
136
|
+
if (!Object.prototype.hasOwnProperty.call(config.tools, toolName)) {
|
|
137
|
+
return Response.json({ error: "Tool not found" }, { status: 404 });
|
|
138
|
+
}
|
|
139
|
+
const tool = config.tools[toolName];
|
|
140
|
+
let input;
|
|
141
|
+
try {
|
|
142
|
+
input = await req.json();
|
|
143
|
+
} catch {
|
|
144
|
+
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
if (config.onBeforeExecute) {
|
|
148
|
+
await config.onBeforeExecute(toolName, input, req);
|
|
149
|
+
}
|
|
150
|
+
const result = await tool.run(input, {
|
|
151
|
+
isServer: true,
|
|
152
|
+
headers: req.headers,
|
|
153
|
+
...config.context
|
|
154
|
+
});
|
|
155
|
+
return Response.json({ result }, {
|
|
156
|
+
headers: { "Access-Control-Allow-Origin": "*" }
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (error instanceof Error && error.name === "ZodError") {
|
|
160
|
+
return Response.json({ error: error.message }, { status: 400 });
|
|
161
|
+
}
|
|
162
|
+
return Response.json(
|
|
163
|
+
{ error: error instanceof Error ? error.message : "Tool execution failed" },
|
|
164
|
+
{ status: 500 }
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return Response.json({ error: "Not found" }, { status: 404 });
|
|
169
|
+
};
|
|
170
|
+
}
|
|
94
171
|
export {
|
|
95
172
|
generateOpenAPISpec,
|
|
96
|
-
openAPIHandler
|
|
173
|
+
openAPIHandler,
|
|
174
|
+
toolRouter
|
|
97
175
|
};
|