raffel 1.1.48 → 1.1.49
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/docs/ui/assets/raffel-docs.css +234 -12
- package/dist/docs/ui/assets/raffel-docs.js +316 -120
- package/dist/docs/ui/runtime/index.js +316 -120
- package/dist/docs/ui/runtime/index.js.map +1 -1
- package/dist/docs/ui/style-sections/schema-code.d.ts +1 -1
- package/dist/docs/ui/style-sections/schema-code.d.ts.map +1 -1
- package/dist/docs/ui/style-sections/schema-code.js +215 -0
- package/dist/docs/ui/style-sections/schema-code.js.map +1 -1
- package/dist/docs/ui/style-sections/try-it.d.ts +1 -1
- package/dist/docs/ui/style-sections/try-it.d.ts.map +1 -1
- package/dist/docs/ui/style-sections/try-it.js +19 -12
- package/dist/docs/ui/style-sections/try-it.js.map +1 -1
- package/dist/ui/docs/ui/style-sections/schema-code.d.ts +1 -1
- package/dist/ui/docs/ui/style-sections/schema-code.d.ts.map +1 -1
- package/dist/ui/docs/ui/style-sections/try-it.d.ts +1 -1
- package/dist/ui/docs/ui/style-sections/try-it.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1214,60 +1214,191 @@ function generateExampleFromSchema(schema) {
|
|
|
1214
1214
|
default: return null;
|
|
1215
1215
|
}
|
|
1216
1216
|
}
|
|
1217
|
+
function httpBaseUrl() {
|
|
1218
|
+
return String(spec?.servers?.[0]?.url ?? 'http://localhost:3000').replace(/\/$/, '');
|
|
1219
|
+
}
|
|
1220
|
+
// Collect JSON-Schema validation constraints as readable chips.
|
|
1221
|
+
function schemaConstraintChips(schema) {
|
|
1222
|
+
if (!schema || typeof schema !== 'object')
|
|
1223
|
+
return [];
|
|
1224
|
+
const chips = [];
|
|
1225
|
+
if (schema.format)
|
|
1226
|
+
chips.push(`format: ${schema.format}`);
|
|
1227
|
+
if (typeof schema.minLength === 'number')
|
|
1228
|
+
chips.push(`min length: ${schema.minLength}`);
|
|
1229
|
+
if (typeof schema.maxLength === 'number')
|
|
1230
|
+
chips.push(`max length: ${schema.maxLength}`);
|
|
1231
|
+
if (typeof schema.minimum === 'number')
|
|
1232
|
+
chips.push(`>= ${schema.minimum}`);
|
|
1233
|
+
if (typeof schema.exclusiveMinimum === 'number')
|
|
1234
|
+
chips.push(`> ${schema.exclusiveMinimum}`);
|
|
1235
|
+
if (typeof schema.maximum === 'number')
|
|
1236
|
+
chips.push(`<= ${schema.maximum}`);
|
|
1237
|
+
if (typeof schema.exclusiveMaximum === 'number')
|
|
1238
|
+
chips.push(`< ${schema.exclusiveMaximum}`);
|
|
1239
|
+
if (typeof schema.multipleOf === 'number')
|
|
1240
|
+
chips.push(`multiple of ${schema.multipleOf}`);
|
|
1241
|
+
if (typeof schema.minItems === 'number')
|
|
1242
|
+
chips.push(`min items: ${schema.minItems}`);
|
|
1243
|
+
if (typeof schema.maxItems === 'number')
|
|
1244
|
+
chips.push(`max items: ${schema.maxItems}`);
|
|
1245
|
+
if (schema.uniqueItems)
|
|
1246
|
+
chips.push('unique items');
|
|
1247
|
+
if (typeof schema.pattern === 'string')
|
|
1248
|
+
chips.push(`pattern: ${schema.pattern}`);
|
|
1249
|
+
if (schema.nullable)
|
|
1250
|
+
chips.push('nullable');
|
|
1251
|
+
if (schema.readOnly)
|
|
1252
|
+
chips.push('read-only');
|
|
1253
|
+
if (schema.writeOnly)
|
|
1254
|
+
chips.push('write-only');
|
|
1255
|
+
return chips;
|
|
1256
|
+
}
|
|
1257
|
+
// Append constraint / default / enum chips to a row element.
|
|
1258
|
+
function appendConstraintChips(row, schema) {
|
|
1259
|
+
if (!schema || typeof schema !== 'object')
|
|
1260
|
+
return;
|
|
1261
|
+
const chips = schemaConstraintChips(schema);
|
|
1262
|
+
const hasDefault = schema.default !== undefined;
|
|
1263
|
+
const enumValues = Array.isArray(schema.enum) ? schema.enum : [];
|
|
1264
|
+
if (!chips.length && !hasDefault && enumValues.length === 0)
|
|
1265
|
+
return;
|
|
1266
|
+
const wrap = doc.createElement('div');
|
|
1267
|
+
wrap.className = 'constraint-chips';
|
|
1268
|
+
if (hasDefault) {
|
|
1269
|
+
const chip = doc.createElement('span');
|
|
1270
|
+
chip.className = 'constraint-chip';
|
|
1271
|
+
chip.textContent = `default: ${JSON.stringify(schema.default)}`;
|
|
1272
|
+
wrap.appendChild(chip);
|
|
1273
|
+
}
|
|
1274
|
+
chips.forEach(text => {
|
|
1275
|
+
const chip = doc.createElement('span');
|
|
1276
|
+
chip.className = 'constraint-chip';
|
|
1277
|
+
chip.textContent = text;
|
|
1278
|
+
wrap.appendChild(chip);
|
|
1279
|
+
});
|
|
1280
|
+
enumValues.forEach(value => {
|
|
1281
|
+
const chip = doc.createElement('span');
|
|
1282
|
+
chip.className = 'constraint-chip constraint-enum';
|
|
1283
|
+
chip.textContent = String(value);
|
|
1284
|
+
wrap.appendChild(chip);
|
|
1285
|
+
});
|
|
1286
|
+
row.appendChild(wrap);
|
|
1287
|
+
}
|
|
1288
|
+
// Render a JSON value as a Python literal (for the Python request sample).
|
|
1289
|
+
function pyLiteral(value) {
|
|
1290
|
+
if (value === null || value === undefined)
|
|
1291
|
+
return 'None';
|
|
1292
|
+
if (value === true)
|
|
1293
|
+
return 'True';
|
|
1294
|
+
if (value === false)
|
|
1295
|
+
return 'False';
|
|
1296
|
+
if (typeof value === 'number')
|
|
1297
|
+
return String(value);
|
|
1298
|
+
if (typeof value === 'string')
|
|
1299
|
+
return JSON.stringify(value);
|
|
1300
|
+
if (Array.isArray(value))
|
|
1301
|
+
return `[${value.map(pyLiteral).join(', ')}]`;
|
|
1302
|
+
if (typeof value === 'object') {
|
|
1303
|
+
return `{${Object.entries(value).map(([k, v]) => `${JSON.stringify(k)}: ${pyLiteral(v)}`).join(', ')}}`;
|
|
1304
|
+
}
|
|
1305
|
+
return 'None';
|
|
1306
|
+
}
|
|
1307
|
+
// Build request samples in cURL, JavaScript, Python and Rust.
|
|
1308
|
+
function buildHttpSamples(method, url, body) {
|
|
1309
|
+
const hasBody = body !== null && body !== undefined && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(method);
|
|
1310
|
+
const jsonPretty = hasBody ? JSON.stringify(body, null, 2) : '';
|
|
1311
|
+
const samples = {};
|
|
1312
|
+
let curl = `curl -X ${method} "${url}"`;
|
|
1313
|
+
if (hasBody)
|
|
1314
|
+
curl += ` \\\n -H "Content-Type: application/json" \\\n -d '${JSON.stringify(body)}'`;
|
|
1315
|
+
samples.curl = curl;
|
|
1316
|
+
let js = `const res = await fetch(${JSON.stringify(url)}, {\n method: ${JSON.stringify(method)},`;
|
|
1317
|
+
if (hasBody)
|
|
1318
|
+
js += `\n headers: { "Content-Type": "application/json" },\n body: JSON.stringify(${jsonPretty.split('\n').join('\n ')}),`;
|
|
1319
|
+
js += `\n});\nconst data = await res.json();\nconsole.log(data);`;
|
|
1320
|
+
samples.javascript = js;
|
|
1321
|
+
let py = `import requests\n\nres = requests.${method.toLowerCase()}(\n ${JSON.stringify(url)},`;
|
|
1322
|
+
if (hasBody)
|
|
1323
|
+
py += `\n json=${pyLiteral(body)},`;
|
|
1324
|
+
py += `\n)\nprint(res.json())`;
|
|
1325
|
+
samples.python = py;
|
|
1326
|
+
let rust = `use reqwest::Client;\n\nlet client = Client::new();\nlet res = client\n .${method.toLowerCase()}(${JSON.stringify(url)})`;
|
|
1327
|
+
if (hasBody)
|
|
1328
|
+
rust += `\n .json(&serde_json::json!(${jsonPretty.split('\n').join('\n ')}))`;
|
|
1329
|
+
rust += `\n .send()\n .await?;\nlet body = res.text().await?;\nprintln!("{}", body);`;
|
|
1330
|
+
samples.rust = rust;
|
|
1331
|
+
return samples;
|
|
1332
|
+
}
|
|
1217
1333
|
function renderCodeExamples(endpoint, data) {
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
const
|
|
1224
|
-
const
|
|
1225
|
-
const
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
const
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
respCode.textContent = `{}`;
|
|
1265
|
-
}
|
|
1266
|
-
respSection.appendChild(respCode);
|
|
1267
|
-
container.appendChild(respSection);
|
|
1334
|
+
const method = (endpoint.method || 'GET').toUpperCase();
|
|
1335
|
+
const url = `${httpBaseUrl()}${endpoint.path || '/'}`;
|
|
1336
|
+
const reqContent = data.requestBody?.content;
|
|
1337
|
+
const reqSchema = reqContent ? reqContent[Object.keys(reqContent)[0]]?.schema : null;
|
|
1338
|
+
const bodyExample = reqSchema ? generateExampleFromSchema(reqSchema) : null;
|
|
1339
|
+
const samples = buildHttpSamples(method, url, bodyExample);
|
|
1340
|
+
const langs = [['curl', 'cURL'], ['javascript', 'JavaScript'], ['python', 'Python'], ['rust', 'Rust']];
|
|
1341
|
+
const wrap = doc.createElement('div');
|
|
1342
|
+
wrap.className = 'http-code-samples';
|
|
1343
|
+
const tabs = doc.createElement('div');
|
|
1344
|
+
tabs.className = 'code-tabs';
|
|
1345
|
+
const contents = doc.createElement('div');
|
|
1346
|
+
contents.className = 'code-contents';
|
|
1347
|
+
langs.forEach(([key, label], index) => {
|
|
1348
|
+
const tab = doc.createElement('button');
|
|
1349
|
+
tab.type = 'button';
|
|
1350
|
+
tab.className = `code-tab${index === 0 ? ' active' : ''}`;
|
|
1351
|
+
tab.textContent = label;
|
|
1352
|
+
const content = doc.createElement('div');
|
|
1353
|
+
content.className = `code-content${index === 0 ? ' active' : ''}`;
|
|
1354
|
+
const pre = doc.createElement('pre');
|
|
1355
|
+
pre.className = 'http-code-sample-pre';
|
|
1356
|
+
pre.textContent = samples[key];
|
|
1357
|
+
content.appendChild(pre);
|
|
1358
|
+
tab.onclick = () => {
|
|
1359
|
+
tabs.querySelectorAll('.code-tab').forEach((t) => t.classList.remove('active'));
|
|
1360
|
+
contents.querySelectorAll('.code-content').forEach((c) => c.classList.remove('active'));
|
|
1361
|
+
tab.classList.add('active');
|
|
1362
|
+
content.classList.add('active');
|
|
1363
|
+
};
|
|
1364
|
+
tabs.appendChild(tab);
|
|
1365
|
+
contents.appendChild(content);
|
|
1366
|
+
});
|
|
1367
|
+
const copy = doc.createElement('button');
|
|
1368
|
+
copy.type = 'button';
|
|
1369
|
+
copy.className = 'http-code-copy';
|
|
1370
|
+
copy.textContent = 'Copy';
|
|
1371
|
+
copy.onclick = () => {
|
|
1372
|
+
const active = contents.querySelector('.code-content.active pre');
|
|
1373
|
+
const text = active?.textContent ?? '';
|
|
1374
|
+
const clip = globalThis.navigator?.clipboard;
|
|
1375
|
+
if (clip?.writeText) {
|
|
1376
|
+
clip.writeText(text).then(() => {
|
|
1377
|
+
copy.textContent = 'Copied';
|
|
1378
|
+
setTimeout(() => { copy.textContent = 'Copy'; }, 1200);
|
|
1379
|
+
});
|
|
1268
1380
|
}
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1381
|
+
};
|
|
1382
|
+
tabs.appendChild(copy);
|
|
1383
|
+
wrap.appendChild(tabs);
|
|
1384
|
+
wrap.appendChild(contents);
|
|
1385
|
+
return wrap;
|
|
1386
|
+
}
|
|
1387
|
+
// Append a JSON example block to a container.
|
|
1388
|
+
function appendJsonExample(container, value, label = 'Example') {
|
|
1389
|
+
if (value === null || value === undefined)
|
|
1390
|
+
return;
|
|
1391
|
+
const head = doc.createElement('div');
|
|
1392
|
+
head.className = 'response-subhead';
|
|
1393
|
+
head.style.marginTop = '12px';
|
|
1394
|
+
head.textContent = label;
|
|
1395
|
+
container.appendChild(head);
|
|
1396
|
+
const pre = doc.createElement('pre');
|
|
1397
|
+
pre.className = 'http-code-sample-pre';
|
|
1398
|
+
pre.style.border = '1px solid var(--border-color)';
|
|
1399
|
+
pre.style.borderRadius = '8px';
|
|
1400
|
+
pre.textContent = JSON.stringify(value, null, 2);
|
|
1401
|
+
container.appendChild(pre);
|
|
1271
1402
|
}
|
|
1272
1403
|
function renderSchemaTree(parent, schema, depth = 0) {
|
|
1273
1404
|
if (!schema)
|
|
@@ -1290,8 +1421,7 @@ function renderSchemaTree(parent, schema, depth = 0) {
|
|
|
1290
1421
|
row.style.paddingLeft = depth === 0 ? '8px' : '0';
|
|
1291
1422
|
const type = prop.type || 'any';
|
|
1292
1423
|
const required = schema.required?.includes(key) ? '<span style="color: #ef4444; margin-left: 4px;">*</span>' : '';
|
|
1293
|
-
|
|
1294
|
-
row.innerHTML = `<div style="font-weight: 500; color: var(--text-primary); display: flex; align-items: center;">${esc(key)}${required} <span style="color: var(--text-secondary); font-weight: normal; margin-left: 8px;">${type}${format}</span></div>`;
|
|
1424
|
+
row.innerHTML = `<div style="font-weight: 500; color: var(--text-primary); display: flex; align-items: center;">${esc(key)}${required} <span class="schema-type type-${esc(prop.type || 'null')}" style="margin-left: 8px;">${esc(type)}</span></div>`;
|
|
1295
1425
|
if (prop.description) {
|
|
1296
1426
|
const desc = doc.createElement('div');
|
|
1297
1427
|
desc.style.fontSize = '12px';
|
|
@@ -1300,33 +1430,20 @@ function renderSchemaTree(parent, schema, depth = 0) {
|
|
|
1300
1430
|
desc.textContent = prop.description;
|
|
1301
1431
|
row.appendChild(desc);
|
|
1302
1432
|
}
|
|
1303
|
-
|
|
1304
|
-
const ex = doc.createElement('div');
|
|
1305
|
-
ex.style.fontSize = '11px';
|
|
1306
|
-
ex.style.color = '#888';
|
|
1307
|
-
ex.style.marginTop = '2px';
|
|
1308
|
-
ex.style.fontFamily = 'monospace';
|
|
1309
|
-
ex.textContent = `Example: ${JSON.stringify(prop.example)}`;
|
|
1310
|
-
row.appendChild(ex);
|
|
1311
|
-
}
|
|
1312
|
-
if (prop.enum) {
|
|
1313
|
-
const en = doc.createElement('div');
|
|
1314
|
-
en.style.fontSize = '11px';
|
|
1315
|
-
en.style.color = '#888';
|
|
1316
|
-
en.style.marginTop = '2px';
|
|
1317
|
-
en.textContent = `Values: ${prop.enum.join(', ')}`;
|
|
1318
|
-
row.appendChild(en);
|
|
1319
|
-
}
|
|
1433
|
+
appendConstraintChips(row, prop);
|
|
1320
1434
|
div.appendChild(row);
|
|
1321
|
-
if (prop.properties) {
|
|
1435
|
+
if (prop.type === 'object' && prop.properties) {
|
|
1322
1436
|
renderSchemaTree(div, prop, depth + 1);
|
|
1323
1437
|
}
|
|
1438
|
+
else if (prop.type === 'array' && prop.items?.properties) {
|
|
1439
|
+
renderSchemaTree(div, prop.items, depth + 1);
|
|
1440
|
+
}
|
|
1324
1441
|
});
|
|
1325
1442
|
}
|
|
1326
1443
|
else if (schema.type === 'array' && schema.items) {
|
|
1327
1444
|
const row = doc.createElement('div');
|
|
1328
1445
|
row.style.padding = '6px 0';
|
|
1329
|
-
row.innerHTML = `<span style="color: var(--text-primary); font-weight: 500;">array</span> <span style="color: var(--text-secondary);">of ${schema.items.type || 'object'}</span>`;
|
|
1446
|
+
row.innerHTML = `<span style="color: var(--text-primary); font-weight: 500;">array</span> <span style="color: var(--text-secondary);">of ${esc(schema.items.type || 'object')}</span>`;
|
|
1330
1447
|
div.appendChild(row);
|
|
1331
1448
|
if (schema.items.properties) {
|
|
1332
1449
|
renderSchemaTree(div, schema.items, depth + 1);
|
|
@@ -1336,10 +1453,108 @@ function renderSchemaTree(parent, schema, depth = 0) {
|
|
|
1336
1453
|
const row = doc.createElement('div');
|
|
1337
1454
|
row.style.padding = '6px 0';
|
|
1338
1455
|
row.innerHTML = `<span style="color: var(--text-secondary);">${esc(schema.type || 'any')}</span>`;
|
|
1456
|
+
appendConstraintChips(row, schema);
|
|
1339
1457
|
div.appendChild(row);
|
|
1340
1458
|
}
|
|
1341
1459
|
parent.appendChild(div);
|
|
1342
1460
|
}
|
|
1461
|
+
// Render one HTTP parameter group (path/query/header/cookie) ReDoc-style.
|
|
1462
|
+
function renderParamGroup(title, params) {
|
|
1463
|
+
const group = doc.createElement('div');
|
|
1464
|
+
group.className = 'http-param-group';
|
|
1465
|
+
const heading = doc.createElement('div');
|
|
1466
|
+
heading.className = 'http-param-group-title';
|
|
1467
|
+
heading.textContent = title;
|
|
1468
|
+
group.appendChild(heading);
|
|
1469
|
+
const list = doc.createElement('div');
|
|
1470
|
+
list.className = 'http-params';
|
|
1471
|
+
params.forEach(param => {
|
|
1472
|
+
const item = doc.createElement('div');
|
|
1473
|
+
item.className = 'http-param';
|
|
1474
|
+
const schema = param.schema || {};
|
|
1475
|
+
const typeName = schema.type === 'array' ? `${schema.items?.type || 'any'}[]` : (schema.type || 'string');
|
|
1476
|
+
const head = doc.createElement('div');
|
|
1477
|
+
head.className = 'http-param-head';
|
|
1478
|
+
head.innerHTML = `<span class="http-param-name">${esc(param.name)}</span>` +
|
|
1479
|
+
`<span class="schema-type type-${esc(schema.type || 'string')}">${esc(typeName)}</span>` +
|
|
1480
|
+
(param.required ? '<span class="http-param-required">required</span>' : '') +
|
|
1481
|
+
(param.deprecated ? '<span class="http-param-deprecated">deprecated</span>' : '');
|
|
1482
|
+
item.appendChild(head);
|
|
1483
|
+
const description = param.description || schema.description;
|
|
1484
|
+
if (description) {
|
|
1485
|
+
const desc = doc.createElement('div');
|
|
1486
|
+
desc.className = 'http-param-desc';
|
|
1487
|
+
desc.textContent = description;
|
|
1488
|
+
item.appendChild(desc);
|
|
1489
|
+
}
|
|
1490
|
+
appendConstraintChips(item, schema);
|
|
1491
|
+
list.appendChild(item);
|
|
1492
|
+
});
|
|
1493
|
+
group.appendChild(list);
|
|
1494
|
+
return group;
|
|
1495
|
+
}
|
|
1496
|
+
// Render a single response as a collapsible accordion (ReDoc-style).
|
|
1497
|
+
function renderResponseAccordion(status, resp, openByDefault) {
|
|
1498
|
+
const statusClass = status.startsWith('2') ? 'status-2xx'
|
|
1499
|
+
: status.startsWith('3') ? 'status-3xx'
|
|
1500
|
+
: status.startsWith('4') ? 'status-4xx'
|
|
1501
|
+
: 'status-5xx';
|
|
1502
|
+
const acc = doc.createElement('div');
|
|
1503
|
+
acc.className = `response-accordion${openByDefault ? ' open' : ''}`;
|
|
1504
|
+
const header = doc.createElement('button');
|
|
1505
|
+
header.type = 'button';
|
|
1506
|
+
header.className = 'response-accordion-header';
|
|
1507
|
+
header.innerHTML = `<span class="response-accordion-caret">▶</span>` +
|
|
1508
|
+
`<span class="response-status-dot ${statusClass}"></span>` +
|
|
1509
|
+
`<span class="response-status-code">${esc(status)}</span>` +
|
|
1510
|
+
`<span class="response-status-desc">${esc(resp.description || '')}</span>`;
|
|
1511
|
+
const body = doc.createElement('div');
|
|
1512
|
+
body.className = 'response-accordion-body';
|
|
1513
|
+
if (resp.headers && Object.keys(resp.headers).length > 0) {
|
|
1514
|
+
const block = doc.createElement('div');
|
|
1515
|
+
block.className = 'response-block';
|
|
1516
|
+
const sub = doc.createElement('div');
|
|
1517
|
+
sub.className = 'response-subhead';
|
|
1518
|
+
sub.textContent = 'Response Headers';
|
|
1519
|
+
block.appendChild(sub);
|
|
1520
|
+
const list = doc.createElement('div');
|
|
1521
|
+
list.className = 'http-params';
|
|
1522
|
+
Object.entries(resp.headers).forEach(([name, def]) => {
|
|
1523
|
+
const item = doc.createElement('div');
|
|
1524
|
+
item.className = 'http-param';
|
|
1525
|
+
const hschema = def.schema || {};
|
|
1526
|
+
item.innerHTML = `<div class="http-param-head"><span class="http-param-name">${esc(name)}</span><span class="schema-type type-${esc(hschema.type || 'string')}">${esc(hschema.type || 'string')}</span></div>` +
|
|
1527
|
+
(def.description ? `<div class="http-param-desc">${esc(def.description)}</div>` : '');
|
|
1528
|
+
list.appendChild(item);
|
|
1529
|
+
});
|
|
1530
|
+
block.appendChild(list);
|
|
1531
|
+
body.appendChild(block);
|
|
1532
|
+
}
|
|
1533
|
+
const content = resp.content;
|
|
1534
|
+
const contentType = content ? Object.keys(content)[0] : null;
|
|
1535
|
+
const schema = contentType ? content[contentType]?.schema : null;
|
|
1536
|
+
if (schema) {
|
|
1537
|
+
const block = doc.createElement('div');
|
|
1538
|
+
block.className = 'response-block';
|
|
1539
|
+
const sub = doc.createElement('div');
|
|
1540
|
+
sub.className = 'response-subhead';
|
|
1541
|
+
sub.textContent = `Response Body${contentType ? ` · ${contentType}` : ''}`;
|
|
1542
|
+
block.appendChild(sub);
|
|
1543
|
+
renderSchemaTree(block, schema);
|
|
1544
|
+
appendJsonExample(block, generateExampleFromSchema(schema));
|
|
1545
|
+
body.appendChild(block);
|
|
1546
|
+
}
|
|
1547
|
+
if (!body.children.length) {
|
|
1548
|
+
const empty = doc.createElement('div');
|
|
1549
|
+
empty.className = 'response-desc-only';
|
|
1550
|
+
empty.textContent = resp.description || 'No content.';
|
|
1551
|
+
body.appendChild(empty);
|
|
1552
|
+
}
|
|
1553
|
+
header.onclick = () => acc.classList.toggle('open');
|
|
1554
|
+
acc.appendChild(header);
|
|
1555
|
+
acc.appendChild(body);
|
|
1556
|
+
return acc;
|
|
1557
|
+
}
|
|
1343
1558
|
function renderEndpointDetails(endpoint) {
|
|
1344
1559
|
const container = doc.createElement('div');
|
|
1345
1560
|
container.className = 'endpoint-details';
|
|
@@ -1352,22 +1567,29 @@ function renderEndpointDetails(endpoint) {
|
|
|
1352
1567
|
const section = doc.createElement('div');
|
|
1353
1568
|
section.className = 'endpoint-subsection';
|
|
1354
1569
|
section.innerHTML = '<div class="subsection-label">PARAMETERS</div>';
|
|
1355
|
-
const
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1570
|
+
const groups = [
|
|
1571
|
+
['path', 'Path Parameters'],
|
|
1572
|
+
['query', 'Query Parameters'],
|
|
1573
|
+
['header', 'Header Parameters'],
|
|
1574
|
+
['cookie', 'Cookie Parameters'],
|
|
1575
|
+
];
|
|
1576
|
+
groups.forEach(([location, title]) => {
|
|
1577
|
+
const inGroup = params.filter(p => (p.in || 'query') === location);
|
|
1578
|
+
if (inGroup.length > 0)
|
|
1579
|
+
section.appendChild(renderParamGroup(title, inGroup));
|
|
1359
1580
|
});
|
|
1360
|
-
section.appendChild(grid);
|
|
1361
1581
|
container.appendChild(section);
|
|
1362
1582
|
}
|
|
1363
1583
|
const reqBody = data.requestBody;
|
|
1364
1584
|
if (reqBody?.content) {
|
|
1365
1585
|
const section = doc.createElement('div');
|
|
1366
1586
|
section.className = 'endpoint-subsection';
|
|
1367
|
-
|
|
1368
|
-
|
|
1587
|
+
const contentType = Object.keys(reqBody.content)[0];
|
|
1588
|
+
section.innerHTML = `<div class="subsection-label">REQUEST BODY${reqBody.required ? ' <span style="color:#ef4444">required</span>' : ''}${contentType ? ` · ${esc(contentType)}` : ''}</div>`;
|
|
1589
|
+
const content = reqBody.content[contentType];
|
|
1369
1590
|
if (content?.schema) {
|
|
1370
1591
|
renderSchemaTree(section, content.schema);
|
|
1592
|
+
appendJsonExample(section, generateExampleFromSchema(content.schema));
|
|
1371
1593
|
}
|
|
1372
1594
|
container.appendChild(section);
|
|
1373
1595
|
}
|
|
@@ -1376,51 +1598,25 @@ function renderEndpointDetails(endpoint) {
|
|
|
1376
1598
|
const section = doc.createElement('div');
|
|
1377
1599
|
section.className = 'endpoint-subsection';
|
|
1378
1600
|
section.innerHTML = '<div class="subsection-label">RESPONSES</div>';
|
|
1379
|
-
Object.entries(responses)
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
item.innerHTML = `<div style="color: var(--text-secondary); font-size: 14px; margin-bottom: 12px; display: flex; align-items: center; gap: 8px;"><span class="badge badge-${statusClass}">${status}</span> <strong>${esc(resp.description || '')}</strong></div>`;
|
|
1387
|
-
// Headers
|
|
1388
|
-
if (resp.headers && Object.keys(resp.headers).length > 0) {
|
|
1389
|
-
const headersDiv = doc.createElement('div');
|
|
1390
|
-
headersDiv.style.marginBottom = '12px';
|
|
1391
|
-
headersDiv.innerHTML = '<div style="font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-bottom: 6px;">HEADERS</div>';
|
|
1392
|
-
const headersList = doc.createElement('div');
|
|
1393
|
-
headersList.style.fontSize = '12px';
|
|
1394
|
-
Object.entries(resp.headers).forEach(([headerName, headerDef]) => {
|
|
1395
|
-
const headerRow = doc.createElement('div');
|
|
1396
|
-
headerRow.style.padding = '4px 0';
|
|
1397
|
-
headerRow.style.borderLeft = '2px solid var(--border-color)';
|
|
1398
|
-
headerRow.style.paddingLeft = '8px';
|
|
1399
|
-
const headerType = headerDef.schema?.type || 'string';
|
|
1400
|
-
headerRow.innerHTML = `<div style="font-weight: 500; color: var(--text-primary);">${esc(headerName)}</div><div style="color: var(--text-muted); font-size: 11px;">${headerType}</div>${headerDef.description ? `<div style="color: var(--text-muted); font-size: 11px; margin-top: 2px;">${esc(headerDef.description)}</div>` : ''}`;
|
|
1401
|
-
headersList.appendChild(headerRow);
|
|
1402
|
-
});
|
|
1403
|
-
headersDiv.appendChild(headersList);
|
|
1404
|
-
item.appendChild(headersDiv);
|
|
1405
|
-
}
|
|
1406
|
-
// Schema
|
|
1407
|
-
if (resp.content) {
|
|
1408
|
-
const contentType = Object.keys(resp.content)[0];
|
|
1409
|
-
const schema = resp.content[contentType]?.schema;
|
|
1410
|
-
if (schema) {
|
|
1411
|
-
const schemaDiv = doc.createElement('div');
|
|
1412
|
-
schemaDiv.innerHTML = '<div style="font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-bottom: 6px;">SCHEMA</div>';
|
|
1413
|
-
renderSchemaTree(schemaDiv, schema);
|
|
1414
|
-
item.appendChild(schemaDiv);
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
section.appendChild(item);
|
|
1601
|
+
const entries = Object.entries(responses);
|
|
1602
|
+
let opened = false;
|
|
1603
|
+
entries.forEach(([status, resp]) => {
|
|
1604
|
+
const open = !opened && status.startsWith('2');
|
|
1605
|
+
if (open)
|
|
1606
|
+
opened = true;
|
|
1607
|
+
section.appendChild(renderResponseAccordion(status, resp, open));
|
|
1418
1608
|
});
|
|
1609
|
+
if (!opened) {
|
|
1610
|
+
const first = section.querySelector('.response-accordion');
|
|
1611
|
+
if (first)
|
|
1612
|
+
first.classList.add('open');
|
|
1613
|
+
}
|
|
1419
1614
|
container.appendChild(section);
|
|
1420
1615
|
}
|
|
1421
|
-
//
|
|
1616
|
+
// Request samples (cURL / JavaScript / Python / Rust)
|
|
1422
1617
|
const examplesSection = doc.createElement('div');
|
|
1423
1618
|
examplesSection.className = 'endpoint-subsection';
|
|
1619
|
+
examplesSection.innerHTML = '<div class="subsection-label">REQUEST SAMPLES</div>';
|
|
1424
1620
|
examplesSection.appendChild(renderCodeExamples(endpoint, data));
|
|
1425
1621
|
container.appendChild(examplesSection);
|
|
1426
1622
|
}
|