@clidey/dory 0.9.0 → 0.11.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.
@@ -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
@@ -2,6 +2,9 @@ FROM node:22.16-alpine
2
2
 
3
3
  WORKDIR /app
4
4
 
5
+ # Install git
6
+ RUN apk add --no-cache git
7
+
5
8
  COPY ./k8s/config/nginx-default.conf /app/k8s/config/nginx-default.conf
6
9
 
7
10
  # Install dory globally
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@clidey/dory",
3
3
  "private": false,
4
- "version": "0.9.0",
4
+ "version": "0.11.0",
5
5
  "type": "module",
6
6
  "description": "A CLI tool for building and previewing documentation sites",
7
7
  "keywords": [
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ // Read dory.json to get navigation order (flat list, in order)
11
+ function getNavigationOrder(docsDir) {
12
+ const doryJsonPath = path.join(docsDir, 'dory.json');
13
+ if (!fs.existsSync(doryJsonPath)) return [];
14
+ const doryJson = JSON.parse(fs.readFileSync(doryJsonPath, 'utf-8'));
15
+ const order = [];
16
+
17
+ function walkPages(pages) {
18
+ for (const page of pages) {
19
+ if (typeof page === 'string') {
20
+ order.push(page);
21
+ } else if (typeof page === 'object' && page.pages) {
22
+ walkPages(page.pages);
23
+ }
24
+ }
25
+ }
26
+
27
+ if (doryJson.navigation && Array.isArray(doryJson.navigation.tabs)) {
28
+ for (const tab of doryJson.navigation.tabs) {
29
+ if (tab.groups && Array.isArray(tab.groups)) {
30
+ for (const group of tab.groups) {
31
+ if (group.pages && Array.isArray(group.pages)) {
32
+ walkPages(group.pages);
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ return order;
39
+ }
40
+
41
+ // Function to parse frontmatter from MDX content
42
+ function parseFrontmatter(content) {
43
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
44
+ if (!frontmatterMatch) return { data: {}, content };
45
+
46
+ const frontmatter = frontmatterMatch[1];
47
+ const data = {};
48
+
49
+ frontmatter.split('\n').forEach(line => {
50
+ const [key, ...valueParts] = line.split(':');
51
+ if (key && valueParts.length) {
52
+ const value = valueParts.join(':').trim();
53
+ data[key.trim()] = value.replace(/^["']|["']$/g, '');
54
+ }
55
+ });
56
+
57
+ return { data, content: content.slice(frontmatterMatch[0].length) };
58
+ }
59
+
60
+ // Convert filename to path format used by the store
61
+ function pathFromFilename(filename, docsDir) {
62
+ return filename
63
+ .replace(docsDir, '')
64
+ .replace(/\/?index\.mdx$/, '/')
65
+ .replace(/\.mdx$/, '')
66
+ .replace(/\\/g, '/')
67
+ .toLowerCase();
68
+ }
69
+
70
+ // Function to recursively find all MDX files and map by relative path (without .mdx)
71
+ function findMdxFilesMap(dir, baseDir) {
72
+ const files = {};
73
+ const items = fs.readdirSync(dir, { withFileTypes: true });
74
+
75
+ for (const item of items) {
76
+ const fullPath = path.join(dir, item.name);
77
+ if (item.isDirectory()) {
78
+ Object.assign(files, findMdxFilesMap(fullPath, baseDir));
79
+ } else if (item.isFile() && item.name.endsWith('.mdx')) {
80
+ // Key: relative path without .mdx, with forward slashes
81
+ let rel = path.relative(baseDir, fullPath).replace(/\\/g, '/');
82
+ if (rel.endsWith('.mdx')) rel = rel.slice(0, -4);
83
+ files[rel] = fullPath;
84
+ }
85
+ }
86
+
87
+ return files;
88
+ }
89
+
90
+ // Main function to generate frontmatter JSON
91
+ async function generateFrontmatterJson() {
92
+ const docsDir = path.join(__dirname, '..', 'docs');
93
+ const outputDir = path.join(__dirname, '..', 'dist');
94
+
95
+ // Ensure output directory exists
96
+ if (!fs.existsSync(outputDir)) {
97
+ fs.mkdirSync(outputDir, { recursive: true });
98
+ }
99
+
100
+ const navOrder = getNavigationOrder(docsDir);
101
+ const mdxFilesMap = findMdxFilesMap(docsDir, docsDir);
102
+ const usedKeys = new Set();
103
+ const frontmatterArray = [];
104
+
105
+ // Process files in navigation order first
106
+ for (const navKey of navOrder) {
107
+ // Try both with and without "index"
108
+ let fileKey = navKey;
109
+ if (mdxFilesMap[fileKey]) {
110
+ usedKeys.add(fileKey);
111
+ } else if (mdxFilesMap[`${fileKey}/index`]) {
112
+ fileKey = `${fileKey}/index`;
113
+ usedKeys.add(fileKey);
114
+ } else {
115
+ continue;
116
+ }
117
+
118
+ const filePath = mdxFilesMap[fileKey];
119
+ const rawContent = fs.readFileSync(filePath, 'utf-8');
120
+ const { data } = parseFrontmatter(rawContent);
121
+ const pathname = pathFromFilename(filePath, docsDir);
122
+
123
+ frontmatterArray.push({
124
+ ...data,
125
+ path: pathname
126
+ });
127
+ }
128
+
129
+ // Process any extra files not in navigation order, sorted
130
+ const extraKeys = Object.keys(mdxFilesMap).filter(k => !usedKeys.has(k)).sort();
131
+ for (const fileKey of extraKeys) {
132
+ const filePath = mdxFilesMap[fileKey];
133
+ const rawContent = fs.readFileSync(filePath, 'utf-8');
134
+ const { data } = parseFrontmatter(rawContent);
135
+ const pathname = pathFromFilename(filePath, docsDir);
136
+
137
+ frontmatterArray.push({
138
+ ...data,
139
+ path: pathname
140
+ });
141
+ }
142
+
143
+ // Write to output file
144
+ const jsonContent = JSON.stringify(frontmatterArray, null, 2);
145
+ const outputPath = path.join(outputDir, 'frontmatter.json');
146
+ fs.writeFileSync(outputPath, jsonContent);
147
+
148
+ console.log(`✅ Generated frontmatter JSON file: ${outputPath}`);
149
+ console.log(`📄 Processed ${frontmatterArray.length} MDX files`);
150
+ console.log(`📝 Total JSON size: ${jsonContent.length} characters`);
151
+ }
152
+
153
+ // Run the script
154
+ generateFrontmatterJson().catch(console.error);
@@ -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;
@@ -71,11 +75,70 @@ function parseFrontMatter(content: string) {
71
75
  }
72
76
 
73
77
  export let completeFrontMatter: Record<string, any>[] = [];
78
+ let preloadedFrontMatter: Record<string, any>[] | null = null;
79
+
80
+ // Preload frontmatter from JSON file (optimized approach)
81
+ export async function preloadFrontmatter() {
82
+ if (preloadedFrontMatter) return; // Already loaded
83
+
84
+ try {
85
+ const response = await fetch('/frontmatter.json');
86
+ if (response.ok) {
87
+ preloadedFrontMatter = await response.json();
88
+ if (preloadedFrontMatter) {
89
+ completeFrontMatter = [...preloadedFrontMatter];
90
+
91
+ // Update navigation titles from preloaded data
92
+ for (const fm of preloadedFrontMatter) {
93
+ updateNavigationTitle(fm.path, fm.title);
94
+ }
95
+
96
+ // Add to search index
97
+ await addPreloadedContentToSearch();
98
+ }
99
+ }
100
+ } catch (error) {
101
+ console.warn('Failed to load prebuilt frontmatter JSON, falling back to dynamic loading:', error);
102
+ }
103
+ }
74
104
 
75
- // Load frontmatter for a specific pathname
105
+ // Add preloaded content to search index
106
+ async function addPreloadedContentToSearch() {
107
+ if (!preloadedFrontMatter) return;
108
+
109
+ const mdxFiles = import.meta.glob<{ default: string }>('../../docs/**/*.mdx', { query: 'raw' });
110
+ const fileEntries = Object.entries(mdxFiles);
111
+
112
+ await Promise.all(
113
+ preloadedFrontMatter.map(async (fm) => {
114
+ const fileEntry = fileEntries.find(([filePath]) => pathFromFilename(filePath) === fm.path);
115
+ if (fileEntry) {
116
+ const [, loader] = fileEntry;
117
+ const rawContent = (await loader()).default;
118
+ const { content } = parseFrontMatter(rawContent);
119
+
120
+ searchIndex.add({
121
+ url: fm.path,
122
+ title: fm.title || '',
123
+ content,
124
+ pageTitle: fm.title || ''
125
+ });
126
+ }
127
+ })
128
+ );
129
+ }
130
+
131
+ // Load frontmatter for a specific pathname (optimized with fallback)
76
132
  export async function loadMDXFrontMatterForPath(pathname: string) {
133
+ // If we have preloaded data, check if this path is already loaded
134
+ if (preloadedFrontMatter && preloadedFrontMatter.find(fm => fm.path === pathname)) {
135
+ return;
136
+ }
137
+
138
+ // Check if already loaded in completeFrontMatter
77
139
  if (completeFrontMatter.find(fm => fm.path === pathname)) return;
78
140
 
141
+ // Fallback to dynamic loading
79
142
  const mdxFiles = import.meta.glob<{ default: string }>('../../docs/**/*.mdx', { query: 'raw' });
80
143
  const fileEntries = Object.entries(mdxFiles);
81
144
 
@@ -99,8 +162,14 @@ export async function loadMDXFrontMatterForPath(pathname: string) {
99
162
  completeFrontMatter = [...completeFrontMatter, frontmatterItem];
100
163
  }
101
164
 
102
- // Load all remaining frontmatter (after initial load)
165
+ // Load all remaining frontmatter (after initial load) - optimized with fallback
103
166
  export async function loadAllMDXFrontMatter(pathname: string) {
167
+ // If we have preloaded data, we're already done
168
+ if (preloadedFrontMatter) {
169
+ return;
170
+ }
171
+
172
+ // Fallback to dynamic loading
104
173
  const mdxFiles = import.meta.glob<{ default: string }>('../../docs/**/*.mdx', { query: 'raw' });
105
174
  const fileEntries = Object.entries(mdxFiles);
106
175