zero-doc 1.0.15 → 1.0.16

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/cli.js CHANGED
@@ -676,6 +676,11 @@ commander_1.program
676
676
  }
677
677
  });
678
678
  server.listen(CALLBACK_PORT, () => {
679
+ // Display login link
680
+ const reset = '\x1b[0m';
681
+ const blue = '\x1b[34m';
682
+ const underline = '\x1b[4m';
683
+ console.log(`\nšŸ” Login URL: ${blue}${underline}${LOGIN_URL}${reset}\n`);
679
684
  // Open browser silently
680
685
  let openCommand;
681
686
  if (process.platform === 'win32') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zero-doc",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "Zero-Config API Documentation Generator - Generate beautiful API docs from your code automatically",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -8,6 +8,9 @@ interface CodeSnippetProps {
8
8
 
9
9
  const CodeSnippet: React.FC<CodeSnippetProps> = ({ endpoint, baseUrl = 'https://api.example.com' }) => {
10
10
  const [showToast, setShowToast] = useState(false);
11
+ const [isExecuting, setIsExecuting] = useState(false);
12
+ const [response, setResponse] = useState<{ status: number; data: any; headers: Record<string, string> } | null>(null);
13
+ const [showResponse, setShowResponse] = useState(false);
11
14
 
12
15
  const pathParams = endpoint.parameters.filter(p => p.in === 'path');
13
16
  const queryParams = endpoint.parameters.filter(p => p.in === 'query');
@@ -69,6 +72,89 @@ const CodeSnippet: React.FC<CodeSnippetProps> = ({ endpoint, baseUrl = 'https://
69
72
  }
70
73
  };
71
74
 
75
+ const executeRequest = async () => {
76
+ setIsExecuting(true);
77
+ setResponse(null);
78
+ setShowResponse(true);
79
+
80
+ try {
81
+ let actualUrl = endpoint.path;
82
+ pathParams.forEach(param => {
83
+ actualUrl = actualUrl.replace(`:${param.name}`, '1');
84
+ });
85
+
86
+ const actualQueryString = queryParams.length > 0
87
+ ? '?' + queryParams.map(p => `${p.name}=${p.inferredType === 'number' ? '1' : 'value'}`).join('&')
88
+ : '';
89
+
90
+ const cleanBaseUrl = baseUrl.replace(/\/$/, '');
91
+ const cleanPath = actualUrl.startsWith('/') ? actualUrl : `/${actualUrl}`;
92
+ const requestUrl = `${cleanBaseUrl}${cleanPath}${actualQueryString}`;
93
+
94
+ const headers: Record<string, string> = {};
95
+
96
+ if (needsAuth) {
97
+ if (endpoint.authentication?.type === 'bearer') {
98
+ headers['Authorization'] = 'Bearer YOUR_TOKEN_HERE';
99
+ } else if (endpoint.authentication?.type === 'api-key') {
100
+ headers['X-API-Key'] = 'YOUR_API_KEY_HERE';
101
+ } else {
102
+ headers['Authorization'] = 'YOUR_AUTH_TOKEN_HERE';
103
+ }
104
+ }
105
+
106
+ headerParams.forEach(header => {
107
+ headers[header.name] = header.required ? 'REQUIRED_VALUE' : 'OPTIONAL_VALUE';
108
+ });
109
+
110
+ let body: string | undefined;
111
+ if (bodyParams.length > 0) {
112
+ const bodyObj = bodyParams.reduce((acc, param) => {
113
+ const value = param.inferredType === 'number' ? 0 :
114
+ param.inferredType === 'boolean' ? true :
115
+ 'value';
116
+ acc[param.name] = value;
117
+ return acc;
118
+ }, {} as Record<string, any>);
119
+ body = JSON.stringify(bodyObj);
120
+ headers['Content-Type'] = 'application/json';
121
+ }
122
+
123
+ const fetchResponse = await fetch(requestUrl, {
124
+ method: endpoint.method,
125
+ headers,
126
+ body: body,
127
+ });
128
+
129
+ const responseHeaders: Record<string, string> = {};
130
+ fetchResponse.headers.forEach((value, key) => {
131
+ responseHeaders[key] = value;
132
+ });
133
+
134
+ let responseData: any;
135
+ const contentType = fetchResponse.headers.get('content-type');
136
+ if (contentType && contentType.includes('application/json')) {
137
+ responseData = await fetchResponse.json();
138
+ } else {
139
+ responseData = await fetchResponse.text();
140
+ }
141
+
142
+ setResponse({
143
+ status: fetchResponse.status,
144
+ data: responseData,
145
+ headers: responseHeaders,
146
+ });
147
+ } catch (error: any) {
148
+ setResponse({
149
+ status: 0,
150
+ data: { error: error.message || 'Request failed' },
151
+ headers: {},
152
+ });
153
+ } finally {
154
+ setIsExecuting(false);
155
+ }
156
+ };
157
+
72
158
  return (
73
159
  <>
74
160
  <div className="bg-gray-900 rounded-lg border border-gray-700 overflow-hidden shadow-lg">
@@ -81,13 +167,39 @@ const CodeSnippet: React.FC<CodeSnippetProps> = ({ endpoint, baseUrl = 'https://
81
167
  </div>
82
168
  <span className="text-xs font-medium text-gray-400 uppercase tracking-wider">curl</span>
83
169
  </div>
84
- <button
85
- onClick={() => copyToClipboard(curlCommand)}
86
- className="px-3 py-1.5 text-xs text-gray-400 hover:text-white transition-colors rounded hover:bg-gray-700/50 border border-gray-700 hover:border-gray-600"
87
- title="Copy to clipboard"
88
- >
89
- šŸ“‹ Copy
90
- </button>
170
+ <div className="flex items-center gap-2">
171
+ <button
172
+ onClick={executeRequest}
173
+ disabled={isExecuting}
174
+ className="px-3 py-1.5 text-xs text-white bg-blue-600 hover:bg-blue-700 disabled:bg-gray-600 disabled:cursor-not-allowed transition-colors rounded border border-blue-500 hover:border-blue-600 flex items-center gap-1.5"
175
+ title="Try it out"
176
+ >
177
+ {isExecuting ? (
178
+ <>
179
+ <svg className="animate-spin h-3 w-3" fill="none" viewBox="0 0 24 24">
180
+ <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
181
+ <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
182
+ </svg>
183
+ Executing...
184
+ </>
185
+ ) : (
186
+ <>
187
+ <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
188
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
189
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
190
+ </svg>
191
+ Try it out
192
+ </>
193
+ )}
194
+ </button>
195
+ <button
196
+ onClick={() => copyToClipboard(curlCommand)}
197
+ className="px-3 py-1.5 text-xs text-gray-400 hover:text-white transition-colors rounded hover:bg-gray-700/50 border border-gray-700 hover:border-gray-600"
198
+ title="Copy to clipboard"
199
+ >
200
+ šŸ“‹ Copy
201
+ </button>
202
+ </div>
91
203
  </div>
92
204
  <div className="p-5 overflow-x-auto bg-gray-950">
93
205
  <pre className="text-sm text-gray-100 font-mono leading-relaxed whitespace-pre-wrap">
@@ -166,6 +278,47 @@ const CodeSnippet: React.FC<CodeSnippetProps> = ({ endpoint, baseUrl = 'https://
166
278
  </div>
167
279
  </div>
168
280
 
281
+ {/* Response Section */}
282
+ {showResponse && response && (
283
+ <div className="mt-4 bg-gray-900 rounded-lg border border-gray-700 overflow-hidden shadow-lg">
284
+ <div className="flex items-center justify-between px-4 py-3 border-b border-gray-700 bg-gray-800/50">
285
+ <div className="flex items-center gap-2">
286
+ <div className="flex gap-1.5">
287
+ <div className="w-3 h-3 rounded-full bg-red-500"></div>
288
+ <div className="w-3 h-3 rounded-full bg-yellow-500"></div>
289
+ <div className="w-3 h-3 rounded-full bg-green-500"></div>
290
+ </div>
291
+ <span className="text-xs font-medium text-gray-400 uppercase tracking-wider">Response</span>
292
+ <span className={`px-2 py-0.5 rounded text-xs font-semibold ${
293
+ response.status >= 200 && response.status < 300
294
+ ? 'bg-green-900/30 text-green-400'
295
+ : response.status >= 400
296
+ ? 'bg-red-900/30 text-red-400'
297
+ : 'bg-yellow-900/30 text-yellow-400'
298
+ }`}>
299
+ {response.status || 'Error'}
300
+ </span>
301
+ </div>
302
+ <button
303
+ onClick={() => setShowResponse(false)}
304
+ className="px-2 py-1 text-xs text-gray-400 hover:text-white transition-colors rounded hover:bg-gray-700/50"
305
+ title="Close"
306
+ >
307
+ āœ•
308
+ </button>
309
+ </div>
310
+ <div className="p-5 overflow-x-auto bg-gray-950">
311
+ <pre className="text-sm text-gray-100 font-mono leading-relaxed whitespace-pre-wrap">
312
+ <code className="block">
313
+ {typeof response.data === 'string'
314
+ ? response.data
315
+ : JSON.stringify(response.data, null, 2)}
316
+ </code>
317
+ </pre>
318
+ </div>
319
+ </div>
320
+ )}
321
+
169
322
  {/* Toast Notification */}
170
323
  {showToast && (
171
324
  <div className="fixed bottom-4 right-4 bg-green-600 text-white px-4 py-3 rounded-lg shadow-lg flex items-center gap-2 z-50 animate-fade-in">