@clidey/dory 0.9.0 → 0.10.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/.claude/settings.json +29 -0
- package/CLAUDE.md +161 -0
- package/docs/api-reference/asyncapi.json +324 -0
- package/docs/api-reference/websocket-operations/receive-chat-message.mdx +5 -0
- package/docs/api-reference/websocket-operations/receive-notifications.mdx +5 -0
- package/docs/api-reference/websocket-operations/receive-presence-update.mdx +5 -0
- package/docs/api-reference/websocket-operations/send-chat-message.mdx +5 -0
- package/docs/api-reference/websocket-operations/send-presence-update.mdx +5 -0
- package/docs/dory.json +16 -0
- package/docs/websocket-playground-demo.mdx +135 -0
- package/k8s/Dockerfile +3 -0
- package/package.json +1 -1
- package/src/components/store.ts +4 -0
- package/src/mdx/async-api.tsx +369 -0
- package/src/mdx/mdx.tsx +5 -1
- package/src/mdx/websocket-playground.tsx +755 -0
- package/src/ui/layout.tsx +21 -3
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'WebSocket Playground Demo'
|
|
3
|
+
description: 'Interactive WebSocket playground for testing real-time connections'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# WebSocket Playground Demo
|
|
7
|
+
|
|
8
|
+
This page demonstrates the WebSocket playground component with various configuration options and real-time connection testing capabilities.
|
|
9
|
+
|
|
10
|
+
## Basic WebSocket Connection
|
|
11
|
+
|
|
12
|
+
<WebSocketPlayground
|
|
13
|
+
url="/ws/chat"
|
|
14
|
+
title="Basic Chat WebSocket"
|
|
15
|
+
description="Connect to a WebSocket server and send/receive messages in real-time"
|
|
16
|
+
servers={[
|
|
17
|
+
{
|
|
18
|
+
url: "wss://echo.websocket.org",
|
|
19
|
+
description: "WebSocket Echo Server"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
url: "wss://ws.postman-echo.com/raw",
|
|
23
|
+
description: "Postman Echo WebSocket"
|
|
24
|
+
}
|
|
25
|
+
]}
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
## WebSocket with Subprotocols
|
|
29
|
+
|
|
30
|
+
<WebSocketPlayground
|
|
31
|
+
url="/ws/protocol"
|
|
32
|
+
title="WebSocket with Subprotocols"
|
|
33
|
+
description="WebSocket connection with custom subprotocols for specialized communication"
|
|
34
|
+
subprotocols={["chat", "v1.0"]}
|
|
35
|
+
servers={[
|
|
36
|
+
{
|
|
37
|
+
url: "wss://echo.websocket.org",
|
|
38
|
+
description: "Echo Server with Subprotocols"
|
|
39
|
+
}
|
|
40
|
+
]}
|
|
41
|
+
/>
|
|
42
|
+
|
|
43
|
+
## WebSocket with Authentication
|
|
44
|
+
|
|
45
|
+
<WebSocketPlayground
|
|
46
|
+
url="/ws/secure"
|
|
47
|
+
title="Authenticated WebSocket"
|
|
48
|
+
description="WebSocket connection with authentication headers"
|
|
49
|
+
authType="bearer"
|
|
50
|
+
headers={{
|
|
51
|
+
"User-Agent": "Dory WebSocket Client",
|
|
52
|
+
"X-Client-Version": "1.0.0"
|
|
53
|
+
}}
|
|
54
|
+
servers={[
|
|
55
|
+
{
|
|
56
|
+
url: "wss://echo.websocket.org",
|
|
57
|
+
description: "Secure Echo Server"
|
|
58
|
+
}
|
|
59
|
+
]}
|
|
60
|
+
/>
|
|
61
|
+
|
|
62
|
+
## Real-time Chat Example
|
|
63
|
+
|
|
64
|
+
<WebSocketPlayground
|
|
65
|
+
url="/chat"
|
|
66
|
+
title="Real-time Chat"
|
|
67
|
+
description="Connect to a chat server and participate in real-time conversations"
|
|
68
|
+
subprotocols={["chat.v1", "json"]}
|
|
69
|
+
headers={{
|
|
70
|
+
"X-Client-Type": "web",
|
|
71
|
+
"X-Protocol-Version": "1.0"
|
|
72
|
+
}}
|
|
73
|
+
servers={[
|
|
74
|
+
{
|
|
75
|
+
url: "wss://echo.websocket.org",
|
|
76
|
+
description: "Chat Echo Server"
|
|
77
|
+
}
|
|
78
|
+
]}
|
|
79
|
+
/>
|
|
80
|
+
|
|
81
|
+
## Testing Instructions
|
|
82
|
+
|
|
83
|
+
1. **Connect**: Click the "Connect" button to establish a WebSocket connection
|
|
84
|
+
2. **Send Messages**: Type messages in the input field and click "Send" or press Enter
|
|
85
|
+
3. **View Messages**: All sent and received messages appear in the message history with timestamps
|
|
86
|
+
4. **Configuration**: Use the tabs to configure headers, subprotocols, and authentication
|
|
87
|
+
5. **Server Selection**: Choose different servers from the dropdown to test various endpoints
|
|
88
|
+
|
|
89
|
+
## Features
|
|
90
|
+
|
|
91
|
+
- **Real-time Connection**: Live WebSocket connection with status indicators
|
|
92
|
+
- **Message History**: View all sent/received messages with timestamps
|
|
93
|
+
- **Custom Headers**: Add authentication and custom headers
|
|
94
|
+
- **Subprotocols**: Configure WebSocket subprotocols
|
|
95
|
+
- **Multiple Servers**: Easy switching between different WebSocket endpoints
|
|
96
|
+
- **Error Handling**: Clear error messages for connection issues
|
|
97
|
+
- **Binary Support**: Handles both text and binary messages
|
|
98
|
+
|
|
99
|
+
## Message Types
|
|
100
|
+
|
|
101
|
+
The playground supports different message types:
|
|
102
|
+
- **Sent**: Messages you send (blue background)
|
|
103
|
+
- **Received**: Messages from the server (green background)
|
|
104
|
+
- **Error**: Connection or sending errors (red background)
|
|
105
|
+
- **System**: Connection status updates (gray background)
|
|
106
|
+
|
|
107
|
+
## Example Messages
|
|
108
|
+
|
|
109
|
+
Try sending these JSON messages to test structured data:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"type": "chat",
|
|
114
|
+
"message": "Hello, WebSocket!",
|
|
115
|
+
"userId": "user123",
|
|
116
|
+
"timestamp": "2024-01-15T10:30:00Z"
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"type": "presence",
|
|
123
|
+
"status": "online",
|
|
124
|
+
"userId": "user123"
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"type": "notification",
|
|
131
|
+
"title": "Test Notification",
|
|
132
|
+
"message": "This is a test notification",
|
|
133
|
+
"level": "info"
|
|
134
|
+
}
|
|
135
|
+
```
|
package/k8s/Dockerfile
CHANGED
package/package.json
CHANGED
package/src/components/store.ts
CHANGED
|
@@ -11,6 +11,10 @@ export const ALL_OPENAPI = Object.fromEntries(
|
|
|
11
11
|
Object.entries(import.meta.glob<{ default: string }>('../../docs/**/openapi.json', { query: 'raw', eager: true }))
|
|
12
12
|
.map(([path, loader]) => [pathFromFilename(path), loader])
|
|
13
13
|
);
|
|
14
|
+
export const ALL_ASYNCAPI = Object.fromEntries(
|
|
15
|
+
Object.entries(import.meta.glob<{ default: string }>('../../docs/**/asyncapi.json', { query: 'raw', eager: true }))
|
|
16
|
+
.map(([path, loader]) => [pathFromFilename(path), loader])
|
|
17
|
+
);
|
|
14
18
|
|
|
15
19
|
export interface Route {
|
|
16
20
|
path: string;
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { motion } from "motion/react"
|
|
2
|
+
import { useState } from "preact/hooks"
|
|
3
|
+
import { APIGroup } from "./api"
|
|
4
|
+
import { PlayButton } from "./api-playground"
|
|
5
|
+
import { Fence } from "./fence"
|
|
6
|
+
import { Properties, Property } from "./open-api"
|
|
7
|
+
import { Tag } from "./tag"
|
|
8
|
+
import { Col, Row } from "./text"
|
|
9
|
+
import { WebSocketPlayground, type WebSocketServer } from "./websocket-playground"
|
|
10
|
+
|
|
11
|
+
interface AsyncAPIMessage {
|
|
12
|
+
name?: string
|
|
13
|
+
title?: string
|
|
14
|
+
summary?: string
|
|
15
|
+
description?: string
|
|
16
|
+
payload?: Schema
|
|
17
|
+
headers?: Schema
|
|
18
|
+
correlationId?: {
|
|
19
|
+
description?: string
|
|
20
|
+
location?: string
|
|
21
|
+
}
|
|
22
|
+
schemaFormat?: string
|
|
23
|
+
contentType?: string
|
|
24
|
+
examples?: any[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface AsyncAPIOperation {
|
|
28
|
+
operationId?: string
|
|
29
|
+
summary?: string
|
|
30
|
+
description?: string
|
|
31
|
+
tags?: Array<{
|
|
32
|
+
name: string
|
|
33
|
+
description?: string
|
|
34
|
+
}>
|
|
35
|
+
message?: AsyncAPIMessage | {
|
|
36
|
+
oneOf?: AsyncAPIMessage[]
|
|
37
|
+
}
|
|
38
|
+
bindings?: {
|
|
39
|
+
ws?: {
|
|
40
|
+
method?: string
|
|
41
|
+
query?: Schema
|
|
42
|
+
headers?: Schema
|
|
43
|
+
bindingVersion?: string
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface AsyncAPIChannel {
|
|
49
|
+
address?: string
|
|
50
|
+
title?: string
|
|
51
|
+
summary?: string
|
|
52
|
+
description?: string
|
|
53
|
+
subscribe?: AsyncAPIOperation
|
|
54
|
+
publish?: AsyncAPIOperation
|
|
55
|
+
parameters?: Record<string, Parameter>
|
|
56
|
+
messages?: Record<string, AsyncAPIMessage>
|
|
57
|
+
bindings?: {
|
|
58
|
+
ws?: {
|
|
59
|
+
method?: string
|
|
60
|
+
query?: Schema
|
|
61
|
+
headers?: Schema
|
|
62
|
+
bindingVersion?: string
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface Schema {
|
|
68
|
+
$ref?: string
|
|
69
|
+
type?: string
|
|
70
|
+
items?: Schema
|
|
71
|
+
properties?: Record<string, Schema>
|
|
72
|
+
required?: string[]
|
|
73
|
+
description?: string
|
|
74
|
+
default?: any
|
|
75
|
+
example?: any
|
|
76
|
+
format?: string
|
|
77
|
+
enum?: any[]
|
|
78
|
+
oneOf?: Schema[]
|
|
79
|
+
anyOf?: Schema[]
|
|
80
|
+
allOf?: Schema[]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface Parameter {
|
|
84
|
+
description?: string
|
|
85
|
+
schema?: Schema
|
|
86
|
+
location?: string
|
|
87
|
+
default?: any
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface Components {
|
|
91
|
+
schemas?: Record<string, Schema>
|
|
92
|
+
messages?: Record<string, AsyncAPIMessage>
|
|
93
|
+
parameters?: Record<string, Parameter>
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface Info {
|
|
97
|
+
title: string
|
|
98
|
+
version: string
|
|
99
|
+
description?: string
|
|
100
|
+
termsOfService?: string
|
|
101
|
+
contact?: {
|
|
102
|
+
name?: string
|
|
103
|
+
url?: string
|
|
104
|
+
email?: string
|
|
105
|
+
}
|
|
106
|
+
license?: {
|
|
107
|
+
name: string
|
|
108
|
+
url?: string
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface AsyncAPIJson {
|
|
113
|
+
asyncapi: string
|
|
114
|
+
info: Info
|
|
115
|
+
servers?: Record<string, WebSocketServer & {
|
|
116
|
+
host?: string
|
|
117
|
+
pathname?: string
|
|
118
|
+
protocol?: string
|
|
119
|
+
protocolVersion?: string
|
|
120
|
+
description?: string
|
|
121
|
+
title?: string
|
|
122
|
+
summary?: string
|
|
123
|
+
variables?: Record<string, any>
|
|
124
|
+
security?: any[]
|
|
125
|
+
tags?: Array<{
|
|
126
|
+
name: string
|
|
127
|
+
description?: string
|
|
128
|
+
}>
|
|
129
|
+
bindings?: any
|
|
130
|
+
}>
|
|
131
|
+
channels: Record<string, AsyncAPIChannel>
|
|
132
|
+
components?: Components
|
|
133
|
+
defaultContentType?: string
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function resolveSchema(schema: Schema | undefined, components: Components): Schema {
|
|
137
|
+
if (!schema) return {}
|
|
138
|
+
|
|
139
|
+
if (schema.$ref) {
|
|
140
|
+
const refPath = schema.$ref.replace(/^#\/components\/schemas\//, '')
|
|
141
|
+
return resolveSchema(components.schemas?.[refPath], components)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (schema.type === 'array' && schema.items) {
|
|
145
|
+
return {
|
|
146
|
+
...schema,
|
|
147
|
+
items: resolveSchema(schema.items, components)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (schema.properties) {
|
|
152
|
+
const resolvedProps: Record<string, Schema> = {}
|
|
153
|
+
for (const [key, value] of Object.entries(schema.properties)) {
|
|
154
|
+
resolvedProps[key] = resolveSchema(value, components)
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
...schema,
|
|
158
|
+
properties: resolvedProps
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return schema
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function isDirectMessage(message: AsyncAPIMessage | { oneOf?: AsyncAPIMessage[] } | undefined): message is AsyncAPIMessage {
|
|
166
|
+
return message != null && !('oneOf' in message);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function AsyncAPI({
|
|
170
|
+
asyncAPIJson,
|
|
171
|
+
channel: selectedChannel,
|
|
172
|
+
operation: selectedOperation
|
|
173
|
+
}: {
|
|
174
|
+
asyncAPIJson: AsyncAPIJson
|
|
175
|
+
channel: string
|
|
176
|
+
operation: 'subscribe' | 'publish'
|
|
177
|
+
}) {
|
|
178
|
+
const { channels, components, servers } = asyncAPIJson;
|
|
179
|
+
const [showPlayground, setShowPlayground] = useState(false)
|
|
180
|
+
|
|
181
|
+
const handleShowPlayground = () => {
|
|
182
|
+
setShowPlayground(true)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Convert AsyncAPI servers to WebSocket servers
|
|
186
|
+
const wsServers: WebSocketServer[] = servers ? Object.entries(servers).map(([key, server]) => ({
|
|
187
|
+
url: `${server.protocol}://${server.host}${server.pathname || ''}`,
|
|
188
|
+
description: server.description || server.title || key,
|
|
189
|
+
protocol: server.protocol
|
|
190
|
+
})) : [];
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<div className="max-w-none w-full">
|
|
194
|
+
{showPlayground && (
|
|
195
|
+
<motion.div
|
|
196
|
+
initial={{ x: "100%" }}
|
|
197
|
+
animate={{ x: 0 }}
|
|
198
|
+
exit={{ x: "100%" }}
|
|
199
|
+
transition={{ type: "spring", damping: 20 }}
|
|
200
|
+
className="fixed inset-y-0 right-0 w-full max-w-3xl bg-white dark:bg-[#1e1e1e] shadow-xl z-50 overflow-y-auto border-l border-zinc-200 dark:border-zinc-800"
|
|
201
|
+
>
|
|
202
|
+
<div className="p-6">
|
|
203
|
+
<div className="flex justify-between items-center mb-6">
|
|
204
|
+
<h2 className="text-xl font-semibold">WebSocket Playground</h2>
|
|
205
|
+
<button
|
|
206
|
+
onClick={() => setShowPlayground(false)}
|
|
207
|
+
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
|
208
|
+
>
|
|
209
|
+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
210
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
211
|
+
</svg>
|
|
212
|
+
</button>
|
|
213
|
+
</div>
|
|
214
|
+
<WebSocketPlayground
|
|
215
|
+
url={selectedChannel}
|
|
216
|
+
servers={wsServers}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
</motion.div>
|
|
220
|
+
)}
|
|
221
|
+
|
|
222
|
+
{Object.entries(channels).map(([channelPath, channelDetails]) => {
|
|
223
|
+
if (channelPath !== selectedChannel) {
|
|
224
|
+
return null
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const operation = channelDetails[selectedOperation]
|
|
228
|
+
if (!operation) {
|
|
229
|
+
return null
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const { summary, description, message } = operation
|
|
233
|
+
const resolvedMessage = isDirectMessage(message) ? message :
|
|
234
|
+
(message?.oneOf && message.oneOf.length > 0 ? message.oneOf[0] : undefined)
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<div key={channelPath} className="my-8">
|
|
238
|
+
<Row cols={2}>
|
|
239
|
+
<Col>
|
|
240
|
+
<h2 className="flex items-center mt-4">
|
|
241
|
+
<Tag variant="medium" className="mr-2 py-1 px-4 rounded-lg">
|
|
242
|
+
{selectedOperation.toUpperCase()}
|
|
243
|
+
</Tag>
|
|
244
|
+
<span className="ml-2 font-mono">{channelPath}</span>
|
|
245
|
+
</h2>
|
|
246
|
+
|
|
247
|
+
{(summary || description) && (
|
|
248
|
+
<p>{summary || description}</p>
|
|
249
|
+
)}
|
|
250
|
+
|
|
251
|
+
{resolvedMessage && (
|
|
252
|
+
<div className="mt-4">
|
|
253
|
+
<h3>Message</h3>
|
|
254
|
+
{resolvedMessage.title && <h4>{resolvedMessage.title}</h4>}
|
|
255
|
+
{resolvedMessage.description && <p>{resolvedMessage.description}</p>}
|
|
256
|
+
|
|
257
|
+
{resolvedMessage.payload && (
|
|
258
|
+
<div className="mt-4">
|
|
259
|
+
<h4>Payload</h4>
|
|
260
|
+
{(() => {
|
|
261
|
+
const resolvedPayload = resolveSchema(resolvedMessage.payload, components || {})
|
|
262
|
+
return (
|
|
263
|
+
<Properties>
|
|
264
|
+
{Object.entries(resolvedPayload.properties || {}).map(([propName, prop]) => (
|
|
265
|
+
<Property
|
|
266
|
+
key={propName}
|
|
267
|
+
name={propName}
|
|
268
|
+
type={prop.type || 'string'}
|
|
269
|
+
required={resolvedPayload.required?.includes(propName)}
|
|
270
|
+
>
|
|
271
|
+
{prop.description}
|
|
272
|
+
{prop.default !== undefined && (
|
|
273
|
+
<span className="text-gray-500 ml-1">(default: {JSON.stringify(prop.default)})</span>
|
|
274
|
+
)}
|
|
275
|
+
</Property>
|
|
276
|
+
))}
|
|
277
|
+
</Properties>
|
|
278
|
+
)
|
|
279
|
+
})()}
|
|
280
|
+
</div>
|
|
281
|
+
)}
|
|
282
|
+
|
|
283
|
+
{resolvedMessage.headers && (
|
|
284
|
+
<div className="mt-4">
|
|
285
|
+
<h4>Headers</h4>
|
|
286
|
+
{(() => {
|
|
287
|
+
const resolvedHeaders = resolveSchema(resolvedMessage.headers, components || {})
|
|
288
|
+
return (
|
|
289
|
+
<Properties>
|
|
290
|
+
{Object.entries(resolvedHeaders.properties || {}).map(([headerName, header]) => (
|
|
291
|
+
<Property
|
|
292
|
+
key={headerName}
|
|
293
|
+
name={headerName}
|
|
294
|
+
type={header.type || 'string'}
|
|
295
|
+
required={resolvedHeaders.required?.includes(headerName)}
|
|
296
|
+
>
|
|
297
|
+
{header.description}
|
|
298
|
+
</Property>
|
|
299
|
+
))}
|
|
300
|
+
</Properties>
|
|
301
|
+
)
|
|
302
|
+
})()}
|
|
303
|
+
</div>
|
|
304
|
+
)}
|
|
305
|
+
|
|
306
|
+
{resolvedMessage?.examples && resolvedMessage.examples.length > 0 && (
|
|
307
|
+
<div className="mt-4">
|
|
308
|
+
<h4>Examples</h4>
|
|
309
|
+
{resolvedMessage.examples.map((example: any, idx: number) => (
|
|
310
|
+
<Fence key={idx} language="json">
|
|
311
|
+
{JSON.stringify(example, null, 2)}
|
|
312
|
+
</Fence>
|
|
313
|
+
))}
|
|
314
|
+
</div>
|
|
315
|
+
)}
|
|
316
|
+
</div>
|
|
317
|
+
)}
|
|
318
|
+
|
|
319
|
+
{components?.schemas && (
|
|
320
|
+
<div className="mt-4">
|
|
321
|
+
<h3>Schemas</h3>
|
|
322
|
+
{Object.entries(components.schemas).filter(([, schema]) => schema.properties).map(([name, schema]) => (
|
|
323
|
+
<div key={name} className="mt-4">
|
|
324
|
+
<h4>{name}</h4>
|
|
325
|
+
<Properties>
|
|
326
|
+
{Object.entries(schema.properties || {}).map(([propName, prop]) => (
|
|
327
|
+
<Property
|
|
328
|
+
key={propName}
|
|
329
|
+
name={propName}
|
|
330
|
+
type={prop.type || 'string'}
|
|
331
|
+
required={schema.required?.includes(propName)}
|
|
332
|
+
>
|
|
333
|
+
{prop.description}
|
|
334
|
+
</Property>
|
|
335
|
+
))}
|
|
336
|
+
</Properties>
|
|
337
|
+
</div>
|
|
338
|
+
))}
|
|
339
|
+
</div>
|
|
340
|
+
)}
|
|
341
|
+
</Col>
|
|
342
|
+
<Col sticky>
|
|
343
|
+
<button
|
|
344
|
+
onClick={handleShowPlayground}
|
|
345
|
+
className='ml-auto mt-3 flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition bg-sky-600 text-white hover:bg-sky-700'
|
|
346
|
+
>
|
|
347
|
+
Try
|
|
348
|
+
<PlayButton className="h-4 w-4" />
|
|
349
|
+
</button>
|
|
350
|
+
<APIGroup title="Message" tag={selectedOperation.toUpperCase()} label={channelPath}>
|
|
351
|
+
<>
|
|
352
|
+
{resolvedMessage?.payload && (() => {
|
|
353
|
+
const resolvedPayload = resolveSchema(resolvedMessage.payload, components || {})
|
|
354
|
+
return (
|
|
355
|
+
<Fence language="json">
|
|
356
|
+
{JSON.stringify(resolvedPayload, null, 2)}
|
|
357
|
+
</Fence>
|
|
358
|
+
)
|
|
359
|
+
})()}
|
|
360
|
+
</>
|
|
361
|
+
</APIGroup>
|
|
362
|
+
</Col>
|
|
363
|
+
</Row>
|
|
364
|
+
</div>
|
|
365
|
+
)
|
|
366
|
+
})}
|
|
367
|
+
</div>
|
|
368
|
+
)
|
|
369
|
+
}
|
package/src/mdx/mdx.tsx
CHANGED
|
@@ -18,6 +18,8 @@ export { Steps, Step } from './step';
|
|
|
18
18
|
export { Table as table, Th as th, Td as td } from './table';
|
|
19
19
|
export { UnorderedList as ul, OrderedList as ol, ListItem as li } from './list';
|
|
20
20
|
export { APIPlayground } from './api-playground';
|
|
21
|
+
export { WebSocketPlayground } from './websocket-playground';
|
|
22
|
+
export { AsyncAPI } from './async-api';
|
|
21
23
|
export { Source } from './source';
|
|
22
24
|
|
|
23
25
|
export const wrapper = ({ children }: { children: preact.ComponentChildren }) => {
|
|
@@ -40,4 +42,6 @@ export const wrapper = ({ children }: { children: preact.ComponentChildren }) =>
|
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
|
|
43
|
-
export const hr = () => <hr className="my-4 text-gray-500/25 dark:text-gray-200/25" />;
|
|
45
|
+
export const hr = () => <hr className="my-4 text-gray-500/25 dark:text-gray-200/25" />;
|
|
46
|
+
|
|
47
|
+
export const br = () => <br />;
|