@lantos1618/better-ui 0.7.1 → 0.9.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/dist/components/index.js +53 -14
- package/dist/components/index.mjs +54 -15
- package/dist/mcp/index.d.mts +3 -31
- package/dist/mcp/index.d.ts +3 -31
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.mjs +1 -1
- package/dist/openapi/index.d.mts +113 -0
- package/dist/openapi/index.d.ts +113 -0
- package/dist/openapi/index.js +260 -0
- package/dist/openapi/index.mjs +97 -0
- package/dist/schema-DdZf6066.d.mts +32 -0
- package/dist/schema-DdZf6066.d.ts +32 -0
- package/package.json +7 -2
package/dist/components/index.js
CHANGED
|
@@ -179,16 +179,24 @@ function ChatProvider({ endpoint = "/api/chat", tools, toolStateStore: externalS
|
|
|
179
179
|
const [threads, setThreads] = (0, import_react2.useState)([]);
|
|
180
180
|
const [activeThreadId, setActiveThreadId] = (0, import_react2.useState)(threadId);
|
|
181
181
|
(0, import_react2.useEffect)(() => {
|
|
182
|
-
if (persistence)
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
if (!persistence) return;
|
|
183
|
+
let cancelled = false;
|
|
184
|
+
persistence.listThreads().then((t) => {
|
|
185
|
+
if (!cancelled) setThreads(t);
|
|
186
|
+
}).catch((err) => console.warn("[better-ui] persistence error:", err));
|
|
187
|
+
return () => {
|
|
188
|
+
cancelled = true;
|
|
189
|
+
};
|
|
185
190
|
}, [persistence]);
|
|
186
191
|
(0, import_react2.useEffect)(() => {
|
|
187
|
-
if (persistence
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
+
if (!persistence || !activeThreadId) return;
|
|
193
|
+
let cancelled = false;
|
|
194
|
+
persistence.getMessages(activeThreadId).then((msgs) => {
|
|
195
|
+
if (!cancelled) setMessages(msgs);
|
|
196
|
+
}).catch((err) => console.warn("[better-ui] persistence error:", err));
|
|
197
|
+
return () => {
|
|
198
|
+
cancelled = true;
|
|
199
|
+
};
|
|
192
200
|
}, [persistence, activeThreadId, setMessages]);
|
|
193
201
|
const prevStatusRef = (0, import_react2.useRef)(status);
|
|
194
202
|
(0, import_react2.useEffect)(() => {
|
|
@@ -212,9 +220,10 @@ function ChatProvider({ endpoint = "/api/chat", tools, toolStateStore: externalS
|
|
|
212
220
|
}, [persistence, setMessages, toolStateStore]);
|
|
213
221
|
const switchThreadFn = (0, import_react2.useCallback)(async (id) => {
|
|
214
222
|
if (!persistence) throw new Error("Persistence not configured");
|
|
215
|
-
|
|
216
|
-
setMessages([]);
|
|
223
|
+
const msgs = await persistence.getMessages(id);
|
|
217
224
|
toolStateStore.clear();
|
|
225
|
+
setMessages(msgs);
|
|
226
|
+
setActiveThreadId(id);
|
|
218
227
|
}, [persistence, setMessages, toolStateStore]);
|
|
219
228
|
const deleteThreadFn = (0, import_react2.useCallback)(async (id) => {
|
|
220
229
|
if (!persistence) throw new Error("Persistence not configured");
|
|
@@ -237,8 +246,11 @@ function ChatProvider({ endpoint = "/api/chat", tools, toolStateStore: externalS
|
|
|
237
246
|
stateContext[entry.toolName] = entry.output;
|
|
238
247
|
}
|
|
239
248
|
}
|
|
240
|
-
|
|
241
|
-
|
|
249
|
+
if (Object.keys(stateContext).length > 0) {
|
|
250
|
+
dirty.clear();
|
|
251
|
+
return stateContext;
|
|
252
|
+
}
|
|
253
|
+
return null;
|
|
242
254
|
}, [toolStateStore]);
|
|
243
255
|
const executeToolDirect = (0, import_react2.useCallback)(async (toolName, toolInput, toolCallId) => {
|
|
244
256
|
const currentVersion = (versionRef.current.get(toolCallId) || 0) + 1;
|
|
@@ -420,6 +432,33 @@ var import_ai2 = require("ai");
|
|
|
420
432
|
// src/components/ToolResult.tsx
|
|
421
433
|
var import_react4 = require("react");
|
|
422
434
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
435
|
+
var ToolViewErrorBoundary = class extends import_react4.Component {
|
|
436
|
+
constructor() {
|
|
437
|
+
super(...arguments);
|
|
438
|
+
this.state = { error: null };
|
|
439
|
+
}
|
|
440
|
+
static getDerivedStateFromError(error) {
|
|
441
|
+
return { error };
|
|
442
|
+
}
|
|
443
|
+
componentDidCatch(error, info) {
|
|
444
|
+
console.error(`[better-ui] View for tool "${this.props.toolName}" threw:`, error, info);
|
|
445
|
+
}
|
|
446
|
+
render() {
|
|
447
|
+
if (this.state.error) {
|
|
448
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-[var(--bui-error-muted,rgba(220,38,38,0.08))] border border-[var(--bui-error-border,rgba(153,27,27,0.5))] rounded-xl p-4", children: [
|
|
449
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
450
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "w-4 h-4 text-[var(--bui-error-fg,#f87171)] shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" }) }),
|
|
451
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-[var(--bui-error-fg,#f87171)] text-sm font-medium", children: [
|
|
452
|
+
this.props.toolName,
|
|
453
|
+
" view error"
|
|
454
|
+
] })
|
|
455
|
+
] }),
|
|
456
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-[var(--bui-error-fg,#f87171)]/70 text-xs mt-1", children: this.state.error.message })
|
|
457
|
+
] });
|
|
458
|
+
}
|
|
459
|
+
return this.props.children;
|
|
460
|
+
}
|
|
461
|
+
};
|
|
423
462
|
function useIsFollowup(toolStateStore, toolCallId) {
|
|
424
463
|
const subscribe = (0, import_react4.useCallback)(
|
|
425
464
|
(listener) => toolStateStore.subscribeAll(listener),
|
|
@@ -602,7 +641,7 @@ function ToolResult({
|
|
|
602
641
|
)
|
|
603
642
|
] });
|
|
604
643
|
}
|
|
605
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: className || "", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
644
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: className || "", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ToolViewErrorBoundary, { toolName, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
606
645
|
toolDef.View,
|
|
607
646
|
{
|
|
608
647
|
data: resolvedOutput,
|
|
@@ -610,7 +649,7 @@ function ToolResult({
|
|
|
610
649
|
onAction: getOnAction(toolCallId, toolName),
|
|
611
650
|
error: storeState?.error ? new Error(storeState.error) : null
|
|
612
651
|
}
|
|
613
|
-
) });
|
|
652
|
+
) }) });
|
|
614
653
|
}
|
|
615
654
|
|
|
616
655
|
// src/components/Markdown.tsx
|
|
@@ -121,16 +121,24 @@ function ChatProvider({ endpoint = "/api/chat", tools, toolStateStore: externalS
|
|
|
121
121
|
const [threads, setThreads] = useState([]);
|
|
122
122
|
const [activeThreadId, setActiveThreadId] = useState(threadId);
|
|
123
123
|
useEffect(() => {
|
|
124
|
-
if (persistence)
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
if (!persistence) return;
|
|
125
|
+
let cancelled = false;
|
|
126
|
+
persistence.listThreads().then((t) => {
|
|
127
|
+
if (!cancelled) setThreads(t);
|
|
128
|
+
}).catch((err) => console.warn("[better-ui] persistence error:", err));
|
|
129
|
+
return () => {
|
|
130
|
+
cancelled = true;
|
|
131
|
+
};
|
|
127
132
|
}, [persistence]);
|
|
128
133
|
useEffect(() => {
|
|
129
|
-
if (persistence
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
+
if (!persistence || !activeThreadId) return;
|
|
135
|
+
let cancelled = false;
|
|
136
|
+
persistence.getMessages(activeThreadId).then((msgs) => {
|
|
137
|
+
if (!cancelled) setMessages(msgs);
|
|
138
|
+
}).catch((err) => console.warn("[better-ui] persistence error:", err));
|
|
139
|
+
return () => {
|
|
140
|
+
cancelled = true;
|
|
141
|
+
};
|
|
134
142
|
}, [persistence, activeThreadId, setMessages]);
|
|
135
143
|
const prevStatusRef = useRef(status);
|
|
136
144
|
useEffect(() => {
|
|
@@ -154,9 +162,10 @@ function ChatProvider({ endpoint = "/api/chat", tools, toolStateStore: externalS
|
|
|
154
162
|
}, [persistence, setMessages, toolStateStore]);
|
|
155
163
|
const switchThreadFn = useCallback2(async (id) => {
|
|
156
164
|
if (!persistence) throw new Error("Persistence not configured");
|
|
157
|
-
|
|
158
|
-
setMessages([]);
|
|
165
|
+
const msgs = await persistence.getMessages(id);
|
|
159
166
|
toolStateStore.clear();
|
|
167
|
+
setMessages(msgs);
|
|
168
|
+
setActiveThreadId(id);
|
|
160
169
|
}, [persistence, setMessages, toolStateStore]);
|
|
161
170
|
const deleteThreadFn = useCallback2(async (id) => {
|
|
162
171
|
if (!persistence) throw new Error("Persistence not configured");
|
|
@@ -179,8 +188,11 @@ function ChatProvider({ endpoint = "/api/chat", tools, toolStateStore: externalS
|
|
|
179
188
|
stateContext[entry.toolName] = entry.output;
|
|
180
189
|
}
|
|
181
190
|
}
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
if (Object.keys(stateContext).length > 0) {
|
|
192
|
+
dirty.clear();
|
|
193
|
+
return stateContext;
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
184
196
|
}, [toolStateStore]);
|
|
185
197
|
const executeToolDirect = useCallback2(async (toolName, toolInput, toolCallId) => {
|
|
186
198
|
const currentVersion = (versionRef.current.get(toolCallId) || 0) + 1;
|
|
@@ -364,8 +376,35 @@ import {
|
|
|
364
376
|
} from "ai";
|
|
365
377
|
|
|
366
378
|
// src/components/ToolResult.tsx
|
|
367
|
-
import { useEffect as useEffect2, useRef as useRef2, useSyncExternalStore as useSyncExternalStore2, useCallback as useCallback3 } from "react";
|
|
379
|
+
import { Component, useEffect as useEffect2, useRef as useRef2, useSyncExternalStore as useSyncExternalStore2, useCallback as useCallback3 } from "react";
|
|
368
380
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
381
|
+
var ToolViewErrorBoundary = class extends Component {
|
|
382
|
+
constructor() {
|
|
383
|
+
super(...arguments);
|
|
384
|
+
this.state = { error: null };
|
|
385
|
+
}
|
|
386
|
+
static getDerivedStateFromError(error) {
|
|
387
|
+
return { error };
|
|
388
|
+
}
|
|
389
|
+
componentDidCatch(error, info) {
|
|
390
|
+
console.error(`[better-ui] View for tool "${this.props.toolName}" threw:`, error, info);
|
|
391
|
+
}
|
|
392
|
+
render() {
|
|
393
|
+
if (this.state.error) {
|
|
394
|
+
return /* @__PURE__ */ jsxs("div", { className: "bg-[var(--bui-error-muted,rgba(220,38,38,0.08))] border border-[var(--bui-error-border,rgba(153,27,27,0.5))] rounded-xl p-4", children: [
|
|
395
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
396
|
+
/* @__PURE__ */ jsx2("svg", { className: "w-4 h-4 text-[var(--bui-error-fg,#f87171)] shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" }) }),
|
|
397
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[var(--bui-error-fg,#f87171)] text-sm font-medium", children: [
|
|
398
|
+
this.props.toolName,
|
|
399
|
+
" view error"
|
|
400
|
+
] })
|
|
401
|
+
] }),
|
|
402
|
+
/* @__PURE__ */ jsx2("p", { className: "text-[var(--bui-error-fg,#f87171)]/70 text-xs mt-1", children: this.state.error.message })
|
|
403
|
+
] });
|
|
404
|
+
}
|
|
405
|
+
return this.props.children;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
369
408
|
function useIsFollowup(toolStateStore, toolCallId) {
|
|
370
409
|
const subscribe = useCallback3(
|
|
371
410
|
(listener) => toolStateStore.subscribeAll(listener),
|
|
@@ -548,7 +587,7 @@ function ToolResult({
|
|
|
548
587
|
)
|
|
549
588
|
] });
|
|
550
589
|
}
|
|
551
|
-
return /* @__PURE__ */ jsx2("div", { className: className || "", children: /* @__PURE__ */ jsx2(
|
|
590
|
+
return /* @__PURE__ */ jsx2("div", { className: className || "", children: /* @__PURE__ */ jsx2(ToolViewErrorBoundary, { toolName, children: /* @__PURE__ */ jsx2(
|
|
552
591
|
toolDef.View,
|
|
553
592
|
{
|
|
554
593
|
data: resolvedOutput,
|
|
@@ -556,7 +595,7 @@ function ToolResult({
|
|
|
556
595
|
onAction: getOnAction(toolCallId, toolName),
|
|
557
596
|
error: storeState?.error ? new Error(storeState.error) : null
|
|
558
597
|
}
|
|
559
|
-
) });
|
|
598
|
+
) }) });
|
|
560
599
|
}
|
|
561
600
|
|
|
562
601
|
// src/components/Markdown.tsx
|
package/dist/mcp/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { T as Tool, g as ToolContext } from '../tool-yZTixiN2.mjs';
|
|
2
|
-
|
|
2
|
+
export { z as zodToJsonSchema } from '../schema-DdZf6066.mjs';
|
|
3
|
+
import 'zod';
|
|
3
4
|
import 'react';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -127,33 +128,4 @@ declare class MCPServer {
|
|
|
127
128
|
*/
|
|
128
129
|
declare function createMCPServer(config: MCPServerConfig): MCPServer;
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
* Lightweight Zod-to-JSON-Schema converter.
|
|
132
|
-
* Handles common Zod types without requiring zod-to-json-schema dependency.
|
|
133
|
-
*/
|
|
134
|
-
|
|
135
|
-
interface JsonSchema {
|
|
136
|
-
type?: string;
|
|
137
|
-
properties?: Record<string, JsonSchema>;
|
|
138
|
-
required?: string[];
|
|
139
|
-
items?: JsonSchema;
|
|
140
|
-
enum?: unknown[];
|
|
141
|
-
description?: string;
|
|
142
|
-
default?: unknown;
|
|
143
|
-
minimum?: number;
|
|
144
|
-
maximum?: number;
|
|
145
|
-
minLength?: number;
|
|
146
|
-
maxLength?: number;
|
|
147
|
-
pattern?: string;
|
|
148
|
-
format?: string;
|
|
149
|
-
anyOf?: JsonSchema[];
|
|
150
|
-
oneOf?: JsonSchema[];
|
|
151
|
-
nullable?: boolean;
|
|
152
|
-
additionalProperties?: boolean | JsonSchema;
|
|
153
|
-
[key: string]: unknown;
|
|
154
|
-
}
|
|
155
|
-
declare function zodToJsonSchema(schema: z.ZodType | {
|
|
156
|
-
[key: string]: any;
|
|
157
|
-
}): JsonSchema;
|
|
158
|
-
|
|
159
|
-
export { MCPServer, type MCPServerConfig, createMCPServer, zodToJsonSchema };
|
|
131
|
+
export { MCPServer, type MCPServerConfig, createMCPServer };
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { T as Tool, g as ToolContext } from '../tool-yZTixiN2.js';
|
|
2
|
-
|
|
2
|
+
export { z as zodToJsonSchema } from '../schema-DdZf6066.js';
|
|
3
|
+
import 'zod';
|
|
3
4
|
import 'react';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -127,33 +128,4 @@ declare class MCPServer {
|
|
|
127
128
|
*/
|
|
128
129
|
declare function createMCPServer(config: MCPServerConfig): MCPServer;
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
* Lightweight Zod-to-JSON-Schema converter.
|
|
132
|
-
* Handles common Zod types without requiring zod-to-json-schema dependency.
|
|
133
|
-
*/
|
|
134
|
-
|
|
135
|
-
interface JsonSchema {
|
|
136
|
-
type?: string;
|
|
137
|
-
properties?: Record<string, JsonSchema>;
|
|
138
|
-
required?: string[];
|
|
139
|
-
items?: JsonSchema;
|
|
140
|
-
enum?: unknown[];
|
|
141
|
-
description?: string;
|
|
142
|
-
default?: unknown;
|
|
143
|
-
minimum?: number;
|
|
144
|
-
maximum?: number;
|
|
145
|
-
minLength?: number;
|
|
146
|
-
maxLength?: number;
|
|
147
|
-
pattern?: string;
|
|
148
|
-
format?: string;
|
|
149
|
-
anyOf?: JsonSchema[];
|
|
150
|
-
oneOf?: JsonSchema[];
|
|
151
|
-
nullable?: boolean;
|
|
152
|
-
additionalProperties?: boolean | JsonSchema;
|
|
153
|
-
[key: string]: unknown;
|
|
154
|
-
}
|
|
155
|
-
declare function zodToJsonSchema(schema: z.ZodType | {
|
|
156
|
-
[key: string]: any;
|
|
157
|
-
}): JsonSchema;
|
|
158
|
-
|
|
159
|
-
export { MCPServer, type MCPServerConfig, createMCPServer, zodToJsonSchema };
|
|
131
|
+
export { MCPServer, type MCPServerConfig, createMCPServer };
|
package/dist/mcp/index.js
CHANGED
package/dist/mcp/index.mjs
CHANGED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { T as Tool } from '../tool-yZTixiN2.mjs';
|
|
2
|
+
import { J as JsonSchema } from '../schema-DdZf6066.mjs';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import 'react';
|
|
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
|
+
interface OpenAPISpecConfig {
|
|
29
|
+
/** API title */
|
|
30
|
+
title: string;
|
|
31
|
+
/** API version */
|
|
32
|
+
version: string;
|
|
33
|
+
/** Tool registry */
|
|
34
|
+
tools: Record<string, Tool>;
|
|
35
|
+
/** API description */
|
|
36
|
+
description?: string;
|
|
37
|
+
/** Base URL for servers (default: '/') */
|
|
38
|
+
serverUrl?: string;
|
|
39
|
+
/** Base path prefix for tool endpoints (default: '/api/tools') */
|
|
40
|
+
basePath?: string;
|
|
41
|
+
/** Tags to group endpoints */
|
|
42
|
+
tags?: Array<{
|
|
43
|
+
name: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
interface OpenAPISpec {
|
|
48
|
+
openapi: '3.1.0';
|
|
49
|
+
info: {
|
|
50
|
+
title: string;
|
|
51
|
+
version: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
};
|
|
54
|
+
servers: Array<{
|
|
55
|
+
url: string;
|
|
56
|
+
}>;
|
|
57
|
+
paths: Record<string, PathItem>;
|
|
58
|
+
components: {
|
|
59
|
+
schemas: Record<string, JsonSchema>;
|
|
60
|
+
};
|
|
61
|
+
tags?: Array<{
|
|
62
|
+
name: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
interface PathItem {
|
|
67
|
+
post: {
|
|
68
|
+
operationId: string;
|
|
69
|
+
summary: string;
|
|
70
|
+
description?: string;
|
|
71
|
+
tags?: string[];
|
|
72
|
+
requestBody: {
|
|
73
|
+
required: true;
|
|
74
|
+
content: {
|
|
75
|
+
'application/json': {
|
|
76
|
+
schema: {
|
|
77
|
+
$ref: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
responses: Record<string, {
|
|
83
|
+
description: string;
|
|
84
|
+
content?: {
|
|
85
|
+
'application/json': {
|
|
86
|
+
schema: {
|
|
87
|
+
$ref: string;
|
|
88
|
+
} | JsonSchema;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
}>;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Generate an OpenAPI 3.1 spec from a Better UI tool registry.
|
|
96
|
+
*/
|
|
97
|
+
declare function generateOpenAPISpec(config: OpenAPISpecConfig): OpenAPISpec;
|
|
98
|
+
/**
|
|
99
|
+
* Create a request handler that serves the OpenAPI spec as JSON.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // Next.js route: app/api/openapi/route.ts
|
|
104
|
+
* export const GET = openAPIHandler({
|
|
105
|
+
* title: 'My Tools',
|
|
106
|
+
* version: '1.0.0',
|
|
107
|
+
* tools,
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function openAPIHandler(config: OpenAPISpecConfig): (req: Request) => Response;
|
|
112
|
+
|
|
113
|
+
export { type OpenAPISpec, type OpenAPISpecConfig, generateOpenAPISpec, openAPIHandler };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { T as Tool } from '../tool-yZTixiN2.js';
|
|
2
|
+
import { J as JsonSchema } from '../schema-DdZf6066.js';
|
|
3
|
+
import 'zod';
|
|
4
|
+
import 'react';
|
|
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
|
+
interface OpenAPISpecConfig {
|
|
29
|
+
/** API title */
|
|
30
|
+
title: string;
|
|
31
|
+
/** API version */
|
|
32
|
+
version: string;
|
|
33
|
+
/** Tool registry */
|
|
34
|
+
tools: Record<string, Tool>;
|
|
35
|
+
/** API description */
|
|
36
|
+
description?: string;
|
|
37
|
+
/** Base URL for servers (default: '/') */
|
|
38
|
+
serverUrl?: string;
|
|
39
|
+
/** Base path prefix for tool endpoints (default: '/api/tools') */
|
|
40
|
+
basePath?: string;
|
|
41
|
+
/** Tags to group endpoints */
|
|
42
|
+
tags?: Array<{
|
|
43
|
+
name: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
interface OpenAPISpec {
|
|
48
|
+
openapi: '3.1.0';
|
|
49
|
+
info: {
|
|
50
|
+
title: string;
|
|
51
|
+
version: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
};
|
|
54
|
+
servers: Array<{
|
|
55
|
+
url: string;
|
|
56
|
+
}>;
|
|
57
|
+
paths: Record<string, PathItem>;
|
|
58
|
+
components: {
|
|
59
|
+
schemas: Record<string, JsonSchema>;
|
|
60
|
+
};
|
|
61
|
+
tags?: Array<{
|
|
62
|
+
name: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
interface PathItem {
|
|
67
|
+
post: {
|
|
68
|
+
operationId: string;
|
|
69
|
+
summary: string;
|
|
70
|
+
description?: string;
|
|
71
|
+
tags?: string[];
|
|
72
|
+
requestBody: {
|
|
73
|
+
required: true;
|
|
74
|
+
content: {
|
|
75
|
+
'application/json': {
|
|
76
|
+
schema: {
|
|
77
|
+
$ref: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
responses: Record<string, {
|
|
83
|
+
description: string;
|
|
84
|
+
content?: {
|
|
85
|
+
'application/json': {
|
|
86
|
+
schema: {
|
|
87
|
+
$ref: string;
|
|
88
|
+
} | JsonSchema;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
}>;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Generate an OpenAPI 3.1 spec from a Better UI tool registry.
|
|
96
|
+
*/
|
|
97
|
+
declare function generateOpenAPISpec(config: OpenAPISpecConfig): OpenAPISpec;
|
|
98
|
+
/**
|
|
99
|
+
* Create a request handler that serves the OpenAPI spec as JSON.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // Next.js route: app/api/openapi/route.ts
|
|
104
|
+
* export const GET = openAPIHandler({
|
|
105
|
+
* title: 'My Tools',
|
|
106
|
+
* version: '1.0.0',
|
|
107
|
+
* tools,
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function openAPIHandler(config: OpenAPISpecConfig): (req: Request) => Response;
|
|
112
|
+
|
|
113
|
+
export { type OpenAPISpec, type OpenAPISpecConfig, generateOpenAPISpec, openAPIHandler };
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/openapi/index.ts
|
|
21
|
+
var openapi_exports = {};
|
|
22
|
+
__export(openapi_exports, {
|
|
23
|
+
generateOpenAPISpec: () => generateOpenAPISpec,
|
|
24
|
+
openAPIHandler: () => openAPIHandler
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(openapi_exports);
|
|
27
|
+
|
|
28
|
+
// src/mcp/schema.ts
|
|
29
|
+
function zodToJsonSchema(schema) {
|
|
30
|
+
return convert(schema);
|
|
31
|
+
}
|
|
32
|
+
function convert(schema) {
|
|
33
|
+
const def = schema._def;
|
|
34
|
+
const typeName = def?.typeName;
|
|
35
|
+
switch (typeName) {
|
|
36
|
+
case "ZodString":
|
|
37
|
+
return convertString(def);
|
|
38
|
+
case "ZodNumber":
|
|
39
|
+
return convertNumber(def);
|
|
40
|
+
case "ZodBoolean":
|
|
41
|
+
return { type: "boolean" };
|
|
42
|
+
case "ZodNull":
|
|
43
|
+
return { type: "null" };
|
|
44
|
+
case "ZodLiteral":
|
|
45
|
+
return { enum: [def.value] };
|
|
46
|
+
case "ZodEnum":
|
|
47
|
+
return { type: "string", enum: def.values };
|
|
48
|
+
case "ZodNativeEnum":
|
|
49
|
+
return { enum: Object.values(def.values) };
|
|
50
|
+
case "ZodObject":
|
|
51
|
+
return convertObject(def);
|
|
52
|
+
case "ZodArray":
|
|
53
|
+
return convertArray(def);
|
|
54
|
+
case "ZodOptional":
|
|
55
|
+
return convert(def.innerType);
|
|
56
|
+
case "ZodNullable": {
|
|
57
|
+
const inner = convert(def.innerType);
|
|
58
|
+
return { anyOf: [inner, { type: "null" }] };
|
|
59
|
+
}
|
|
60
|
+
case "ZodDefault":
|
|
61
|
+
return { ...convert(def.innerType), default: def.defaultValue() };
|
|
62
|
+
case "ZodUnion":
|
|
63
|
+
return { anyOf: def.options.map((o) => convert(o)) };
|
|
64
|
+
case "ZodDiscriminatedUnion":
|
|
65
|
+
return { oneOf: [...def.options.values()].map((o) => convert(o)) };
|
|
66
|
+
case "ZodRecord":
|
|
67
|
+
return {
|
|
68
|
+
type: "object",
|
|
69
|
+
additionalProperties: convert(def.valueType)
|
|
70
|
+
};
|
|
71
|
+
case "ZodTuple": {
|
|
72
|
+
const items = def.items.map((item) => convert(item));
|
|
73
|
+
return { type: "array", items: items.length === 1 ? items[0] : void 0, prefixItems: items };
|
|
74
|
+
}
|
|
75
|
+
case "ZodEffects":
|
|
76
|
+
return convert(def.schema);
|
|
77
|
+
case "ZodPipeline":
|
|
78
|
+
return convert(def.in);
|
|
79
|
+
case "ZodLazy":
|
|
80
|
+
return convert(def.getter());
|
|
81
|
+
case "ZodAny":
|
|
82
|
+
return {};
|
|
83
|
+
case "ZodUnknown":
|
|
84
|
+
return {};
|
|
85
|
+
default:
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function convertString(def) {
|
|
90
|
+
const schema = { type: "string" };
|
|
91
|
+
if (def.checks) {
|
|
92
|
+
for (const check of def.checks) {
|
|
93
|
+
switch (check.kind) {
|
|
94
|
+
case "min":
|
|
95
|
+
schema.minLength = check.value;
|
|
96
|
+
break;
|
|
97
|
+
case "max":
|
|
98
|
+
schema.maxLength = check.value;
|
|
99
|
+
break;
|
|
100
|
+
case "email":
|
|
101
|
+
schema.format = "email";
|
|
102
|
+
break;
|
|
103
|
+
case "url":
|
|
104
|
+
schema.format = "uri";
|
|
105
|
+
break;
|
|
106
|
+
case "uuid":
|
|
107
|
+
schema.format = "uuid";
|
|
108
|
+
break;
|
|
109
|
+
case "regex":
|
|
110
|
+
schema.pattern = check.regex.source;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (def.description) schema.description = def.description;
|
|
116
|
+
return schema;
|
|
117
|
+
}
|
|
118
|
+
function convertNumber(def) {
|
|
119
|
+
const schema = { type: "number" };
|
|
120
|
+
if (def.checks) {
|
|
121
|
+
for (const check of def.checks) {
|
|
122
|
+
switch (check.kind) {
|
|
123
|
+
case "min":
|
|
124
|
+
schema.minimum = check.value;
|
|
125
|
+
if (check.inclusive === false) schema.exclusiveMinimum = check.value;
|
|
126
|
+
break;
|
|
127
|
+
case "max":
|
|
128
|
+
schema.maximum = check.value;
|
|
129
|
+
if (check.inclusive === false) schema.exclusiveMaximum = check.value;
|
|
130
|
+
break;
|
|
131
|
+
case "int":
|
|
132
|
+
schema.type = "integer";
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (def.description) schema.description = def.description;
|
|
138
|
+
return schema;
|
|
139
|
+
}
|
|
140
|
+
function convertObject(def) {
|
|
141
|
+
const shape = def.shape();
|
|
142
|
+
const properties = {};
|
|
143
|
+
const required = [];
|
|
144
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
145
|
+
properties[key] = convert(value);
|
|
146
|
+
const fieldDef = value._def;
|
|
147
|
+
const isOptional = fieldDef?.typeName === "ZodOptional" || fieldDef?.typeName === "ZodDefault";
|
|
148
|
+
if (!isOptional) {
|
|
149
|
+
required.push(key);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const schema = { type: "object", properties };
|
|
153
|
+
if (required.length > 0) schema.required = required;
|
|
154
|
+
if (def.description) schema.description = def.description;
|
|
155
|
+
return schema;
|
|
156
|
+
}
|
|
157
|
+
function convertArray(def) {
|
|
158
|
+
const schema = {
|
|
159
|
+
type: "array",
|
|
160
|
+
items: convert(def.type)
|
|
161
|
+
};
|
|
162
|
+
if (def.minLength) schema.minItems = def.minLength.value;
|
|
163
|
+
if (def.maxLength) schema.maxItems = def.maxLength.value;
|
|
164
|
+
if (def.description) schema.description = def.description;
|
|
165
|
+
return schema;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/openapi/index.ts
|
|
169
|
+
function generateOpenAPISpec(config) {
|
|
170
|
+
const basePath = config.basePath ?? "/api/tools";
|
|
171
|
+
const paths = {};
|
|
172
|
+
const schemas = {};
|
|
173
|
+
const autoTags = /* @__PURE__ */ new Set();
|
|
174
|
+
for (const [key, tool] of Object.entries(config.tools)) {
|
|
175
|
+
const name = tool.name || key;
|
|
176
|
+
const inputSchemaName = `${name}Input`;
|
|
177
|
+
const outputSchemaName = `${name}Output`;
|
|
178
|
+
schemas[inputSchemaName] = zodToJsonSchema(tool.inputSchema);
|
|
179
|
+
if (tool.outputSchema) {
|
|
180
|
+
schemas[outputSchemaName] = zodToJsonSchema(tool.outputSchema);
|
|
181
|
+
}
|
|
182
|
+
const toolTags = tool.tags.length > 0 ? tool.tags : [name];
|
|
183
|
+
for (const t of toolTags) autoTags.add(t);
|
|
184
|
+
const responseSchema = tool.outputSchema ? { $ref: `#/components/schemas/${outputSchemaName}` } : { type: "object" };
|
|
185
|
+
const hints = [];
|
|
186
|
+
if (tool.hints.destructive) hints.push("destructive");
|
|
187
|
+
if (tool.hints.readOnly) hints.push("read-only");
|
|
188
|
+
if (tool.hints.idempotent) hints.push("idempotent");
|
|
189
|
+
if (tool.requiresConfirmation) hints.push("requires confirmation");
|
|
190
|
+
const hintsStr = hints.length > 0 ? ` [${hints.join(", ")}]` : "";
|
|
191
|
+
paths[`${basePath}/${name}`] = {
|
|
192
|
+
post: {
|
|
193
|
+
operationId: name,
|
|
194
|
+
summary: tool.description || name,
|
|
195
|
+
description: (tool.description || "") + hintsStr,
|
|
196
|
+
tags: tool.tags.length > 0 ? tool.tags : void 0,
|
|
197
|
+
requestBody: {
|
|
198
|
+
required: true,
|
|
199
|
+
content: {
|
|
200
|
+
"application/json": {
|
|
201
|
+
schema: { $ref: `#/components/schemas/${inputSchemaName}` }
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
responses: {
|
|
206
|
+
"200": {
|
|
207
|
+
description: "Successful tool execution",
|
|
208
|
+
content: {
|
|
209
|
+
"application/json": {
|
|
210
|
+
schema: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
result: responseSchema
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
"400": {
|
|
220
|
+
description: "Invalid input (Zod validation error)"
|
|
221
|
+
},
|
|
222
|
+
"404": {
|
|
223
|
+
description: "Tool not found"
|
|
224
|
+
},
|
|
225
|
+
"500": {
|
|
226
|
+
description: "Tool execution failed"
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const tags = config.tags ?? [...autoTags].map((t) => ({ name: t }));
|
|
233
|
+
return {
|
|
234
|
+
openapi: "3.1.0",
|
|
235
|
+
info: {
|
|
236
|
+
title: config.title,
|
|
237
|
+
version: config.version,
|
|
238
|
+
...config.description ? { description: config.description } : {}
|
|
239
|
+
},
|
|
240
|
+
servers: [{ url: config.serverUrl ?? "/" }],
|
|
241
|
+
paths,
|
|
242
|
+
components: { schemas },
|
|
243
|
+
...tags.length > 0 ? { tags } : {}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function openAPIHandler(config) {
|
|
247
|
+
const spec = generateOpenAPISpec(config);
|
|
248
|
+
const json = JSON.stringify(spec, null, 2);
|
|
249
|
+
return () => new Response(json, {
|
|
250
|
+
headers: {
|
|
251
|
+
"Content-Type": "application/json",
|
|
252
|
+
"Access-Control-Allow-Origin": "*"
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
257
|
+
0 && (module.exports = {
|
|
258
|
+
generateOpenAPISpec,
|
|
259
|
+
openAPIHandler
|
|
260
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
zodToJsonSchema
|
|
3
|
+
} from "../chunk-OH73K7I5.mjs";
|
|
4
|
+
import "../chunk-Y6FXYEAI.mjs";
|
|
5
|
+
|
|
6
|
+
// src/openapi/index.ts
|
|
7
|
+
function generateOpenAPISpec(config) {
|
|
8
|
+
const basePath = config.basePath ?? "/api/tools";
|
|
9
|
+
const paths = {};
|
|
10
|
+
const schemas = {};
|
|
11
|
+
const autoTags = /* @__PURE__ */ new Set();
|
|
12
|
+
for (const [key, tool] of Object.entries(config.tools)) {
|
|
13
|
+
const name = tool.name || key;
|
|
14
|
+
const inputSchemaName = `${name}Input`;
|
|
15
|
+
const outputSchemaName = `${name}Output`;
|
|
16
|
+
schemas[inputSchemaName] = zodToJsonSchema(tool.inputSchema);
|
|
17
|
+
if (tool.outputSchema) {
|
|
18
|
+
schemas[outputSchemaName] = zodToJsonSchema(tool.outputSchema);
|
|
19
|
+
}
|
|
20
|
+
const toolTags = tool.tags.length > 0 ? tool.tags : [name];
|
|
21
|
+
for (const t of toolTags) autoTags.add(t);
|
|
22
|
+
const responseSchema = tool.outputSchema ? { $ref: `#/components/schemas/${outputSchemaName}` } : { type: "object" };
|
|
23
|
+
const hints = [];
|
|
24
|
+
if (tool.hints.destructive) hints.push("destructive");
|
|
25
|
+
if (tool.hints.readOnly) hints.push("read-only");
|
|
26
|
+
if (tool.hints.idempotent) hints.push("idempotent");
|
|
27
|
+
if (tool.requiresConfirmation) hints.push("requires confirmation");
|
|
28
|
+
const hintsStr = hints.length > 0 ? ` [${hints.join(", ")}]` : "";
|
|
29
|
+
paths[`${basePath}/${name}`] = {
|
|
30
|
+
post: {
|
|
31
|
+
operationId: name,
|
|
32
|
+
summary: tool.description || name,
|
|
33
|
+
description: (tool.description || "") + hintsStr,
|
|
34
|
+
tags: tool.tags.length > 0 ? tool.tags : void 0,
|
|
35
|
+
requestBody: {
|
|
36
|
+
required: true,
|
|
37
|
+
content: {
|
|
38
|
+
"application/json": {
|
|
39
|
+
schema: { $ref: `#/components/schemas/${inputSchemaName}` }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
responses: {
|
|
44
|
+
"200": {
|
|
45
|
+
description: "Successful tool execution",
|
|
46
|
+
content: {
|
|
47
|
+
"application/json": {
|
|
48
|
+
schema: {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: {
|
|
51
|
+
result: responseSchema
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"400": {
|
|
58
|
+
description: "Invalid input (Zod validation error)"
|
|
59
|
+
},
|
|
60
|
+
"404": {
|
|
61
|
+
description: "Tool not found"
|
|
62
|
+
},
|
|
63
|
+
"500": {
|
|
64
|
+
description: "Tool execution failed"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const tags = config.tags ?? [...autoTags].map((t) => ({ name: t }));
|
|
71
|
+
return {
|
|
72
|
+
openapi: "3.1.0",
|
|
73
|
+
info: {
|
|
74
|
+
title: config.title,
|
|
75
|
+
version: config.version,
|
|
76
|
+
...config.description ? { description: config.description } : {}
|
|
77
|
+
},
|
|
78
|
+
servers: [{ url: config.serverUrl ?? "/" }],
|
|
79
|
+
paths,
|
|
80
|
+
components: { schemas },
|
|
81
|
+
...tags.length > 0 ? { tags } : {}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function openAPIHandler(config) {
|
|
85
|
+
const spec = generateOpenAPISpec(config);
|
|
86
|
+
const json = JSON.stringify(spec, null, 2);
|
|
87
|
+
return () => new Response(json, {
|
|
88
|
+
headers: {
|
|
89
|
+
"Content-Type": "application/json",
|
|
90
|
+
"Access-Control-Allow-Origin": "*"
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
generateOpenAPISpec,
|
|
96
|
+
openAPIHandler
|
|
97
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight Zod-to-JSON-Schema converter.
|
|
5
|
+
* Handles common Zod types without requiring zod-to-json-schema dependency.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface JsonSchema {
|
|
9
|
+
type?: string;
|
|
10
|
+
properties?: Record<string, JsonSchema>;
|
|
11
|
+
required?: string[];
|
|
12
|
+
items?: JsonSchema;
|
|
13
|
+
enum?: unknown[];
|
|
14
|
+
description?: string;
|
|
15
|
+
default?: unknown;
|
|
16
|
+
minimum?: number;
|
|
17
|
+
maximum?: number;
|
|
18
|
+
minLength?: number;
|
|
19
|
+
maxLength?: number;
|
|
20
|
+
pattern?: string;
|
|
21
|
+
format?: string;
|
|
22
|
+
anyOf?: JsonSchema[];
|
|
23
|
+
oneOf?: JsonSchema[];
|
|
24
|
+
nullable?: boolean;
|
|
25
|
+
additionalProperties?: boolean | JsonSchema;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
declare function zodToJsonSchema(schema: z.ZodType | {
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}): JsonSchema;
|
|
31
|
+
|
|
32
|
+
export { type JsonSchema as J, zodToJsonSchema as z };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight Zod-to-JSON-Schema converter.
|
|
5
|
+
* Handles common Zod types without requiring zod-to-json-schema dependency.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface JsonSchema {
|
|
9
|
+
type?: string;
|
|
10
|
+
properties?: Record<string, JsonSchema>;
|
|
11
|
+
required?: string[];
|
|
12
|
+
items?: JsonSchema;
|
|
13
|
+
enum?: unknown[];
|
|
14
|
+
description?: string;
|
|
15
|
+
default?: unknown;
|
|
16
|
+
minimum?: number;
|
|
17
|
+
maximum?: number;
|
|
18
|
+
minLength?: number;
|
|
19
|
+
maxLength?: number;
|
|
20
|
+
pattern?: string;
|
|
21
|
+
format?: string;
|
|
22
|
+
anyOf?: JsonSchema[];
|
|
23
|
+
oneOf?: JsonSchema[];
|
|
24
|
+
nullable?: boolean;
|
|
25
|
+
additionalProperties?: boolean | JsonSchema;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
declare function zodToJsonSchema(schema: z.ZodType | {
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}): JsonSchema;
|
|
31
|
+
|
|
32
|
+
export { type JsonSchema as J, zodToJsonSchema as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lantos1618/better-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "A minimal, type-safe AI-first UI framework for building tools",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -41,6 +41,11 @@
|
|
|
41
41
|
"import": "./dist/agui/index.mjs",
|
|
42
42
|
"require": "./dist/agui/index.js"
|
|
43
43
|
},
|
|
44
|
+
"./openapi": {
|
|
45
|
+
"types": "./dist/openapi/index.d.ts",
|
|
46
|
+
"import": "./dist/openapi/index.mjs",
|
|
47
|
+
"require": "./dist/openapi/index.js"
|
|
48
|
+
},
|
|
44
49
|
"./theme.css": "./src/theme.css"
|
|
45
50
|
},
|
|
46
51
|
"files": [
|
|
@@ -69,7 +74,7 @@
|
|
|
69
74
|
},
|
|
70
75
|
"scripts": {
|
|
71
76
|
"build": "npm run build:lib",
|
|
72
|
-
"build:lib": "tsup src/index.ts src/react/index.ts src/components/index.ts src/auth/index.ts src/persistence/index.ts src/mcp/index.ts src/agui/index.ts --format cjs,esm --dts --clean --tsconfig tsconfig.lib.json",
|
|
77
|
+
"build:lib": "tsup src/index.ts src/react/index.ts src/components/index.ts src/auth/index.ts src/persistence/index.ts src/mcp/index.ts src/agui/index.ts src/openapi/index.ts --format cjs,esm --dts --clean --tsconfig tsconfig.lib.json",
|
|
73
78
|
"test": "jest",
|
|
74
79
|
"type-check": "tsc --noEmit",
|
|
75
80
|
"prepublishOnly": "npm run build:lib"
|