raffel 1.1.59 → 1.1.60-next.edcf9f3
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 +108 -0
- package/dist/docs/ui/assets/raffel-docs.js +89 -9
- package/dist/docs/ui/runtime/index.js +89 -9
- package/dist/docs/ui/runtime/index.js.map +1 -1
- package/dist/docs/ui/style-sections/content.d.ts +1 -1
- package/dist/docs/ui/style-sections/content.d.ts.map +1 -1
- package/dist/docs/ui/style-sections/content.js +108 -0
- package/dist/docs/ui/style-sections/content.js.map +1 -1
- package/dist/middleware/policy/bootstrap.d.ts +32 -0
- package/dist/middleware/policy/bootstrap.d.ts.map +1 -1
- package/dist/middleware/policy/bootstrap.js +11 -0
- package/dist/middleware/policy/bootstrap.js.map +1 -1
- package/dist/middleware/policy/co-located-accumulator.d.ts +63 -0
- package/dist/middleware/policy/co-located-accumulator.d.ts.map +1 -0
- package/dist/middleware/policy/co-located-accumulator.js +148 -0
- package/dist/middleware/policy/co-located-accumulator.js.map +1 -0
- package/dist/server/builder/policy-bridges.d.ts +41 -0
- package/dist/server/builder/policy-bridges.d.ts.map +1 -0
- package/dist/server/builder/policy-bridges.js +96 -0
- package/dist/server/builder/policy-bridges.js.map +1 -0
- package/dist/server/builder/programmatic-registration.d.ts +43 -0
- package/dist/server/builder/programmatic-registration.d.ts.map +1 -0
- package/dist/server/builder/programmatic-registration.js +261 -0
- package/dist/server/builder/programmatic-registration.js.map +1 -0
- package/dist/server/builder.d.ts.map +1 -1
- package/dist/server/builder.js +47 -306
- package/dist/server/builder.js.map +1 -1
- package/dist/server/discovery-utils.d.ts +11 -0
- package/dist/server/discovery-utils.d.ts.map +1 -1
- package/dist/server/discovery-utils.js +41 -50
- package/dist/server/discovery-utils.js.map +1 -1
- package/dist/server/fs-routes/co-located-attach.d.ts +32 -0
- package/dist/server/fs-routes/co-located-attach.d.ts.map +1 -0
- package/dist/server/fs-routes/co-located-attach.js +69 -0
- package/dist/server/fs-routes/co-located-attach.js.map +1 -0
- package/dist/server/fs-routes/loader.d.ts.map +1 -1
- package/dist/server/fs-routes/loader.js +2 -232
- package/dist/server/fs-routes/loader.js.map +1 -1
- package/dist/server/fs-routes/route-naming.d.ts +61 -0
- package/dist/server/fs-routes/route-naming.d.ts.map +1 -0
- package/dist/server/fs-routes/route-naming.js +191 -0
- package/dist/server/fs-routes/route-naming.js.map +1 -0
- package/dist/ui/docs/ui/style-sections/content.d.ts +1 -1
- package/dist/ui/docs/ui/style-sections/content.d.ts.map +1 -1
- package/dist/ui/server/fs-routes/co-located-attach.d.ts +32 -0
- package/dist/ui/server/fs-routes/co-located-attach.d.ts.map +1 -0
- package/dist/ui/server/fs-routes/loader.d.ts.map +1 -1
- package/dist/ui/server/fs-routes/route-naming.d.ts +61 -0
- package/dist/ui/server/fs-routes/route-naming.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -1483,6 +1483,114 @@
|
|
|
1483
1483
|
color: var(--text-color);
|
|
1484
1484
|
}
|
|
1485
1485
|
|
|
1486
|
+
/* ========== ROOT OVERVIEW (OpenAPI landing) ========== */
|
|
1487
|
+
.docs-overview {
|
|
1488
|
+
max-width: 820px;
|
|
1489
|
+
margin-bottom: 8px;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
.docs-overview-header {
|
|
1493
|
+
margin: 0 0 12px 0;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
.docs-overview-title {
|
|
1497
|
+
display: flex;
|
|
1498
|
+
align-items: baseline;
|
|
1499
|
+
flex-wrap: wrap;
|
|
1500
|
+
gap: 12px;
|
|
1501
|
+
font-size: 32px;
|
|
1502
|
+
font-weight: 700;
|
|
1503
|
+
line-height: 1.2;
|
|
1504
|
+
margin: 0;
|
|
1505
|
+
color: var(--text-color);
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
.docs-overview-version {
|
|
1509
|
+
font-size: 13px;
|
|
1510
|
+
font-weight: 600;
|
|
1511
|
+
letter-spacing: 0.02em;
|
|
1512
|
+
padding: 3px 10px;
|
|
1513
|
+
border-radius: 999px;
|
|
1514
|
+
color: var(--accent, var(--primary-color));
|
|
1515
|
+
background: color-mix(in srgb, var(--accent, var(--primary-color)) 14%, transparent);
|
|
1516
|
+
border: 1px solid color-mix(in srgb, var(--accent, var(--primary-color)) 35%, transparent);
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
.docs-overview-meta {
|
|
1520
|
+
display: flex;
|
|
1521
|
+
flex-wrap: wrap;
|
|
1522
|
+
align-items: center;
|
|
1523
|
+
gap: 10px;
|
|
1524
|
+
font-size: 14px;
|
|
1525
|
+
color: var(--text-muted);
|
|
1526
|
+
margin: 0 0 24px 0;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
.docs-overview-meta a {
|
|
1530
|
+
color: var(--accent, var(--primary-color));
|
|
1531
|
+
text-decoration: none;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
.docs-overview-meta a:hover {
|
|
1535
|
+
text-decoration: underline;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
.docs-overview-meta-sep {
|
|
1539
|
+
color: var(--border-color);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
.docs-overview-servers {
|
|
1543
|
+
margin: 0 0 28px 0;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
.docs-overview-subtitle {
|
|
1547
|
+
font-size: 13px;
|
|
1548
|
+
font-weight: 700;
|
|
1549
|
+
text-transform: uppercase;
|
|
1550
|
+
letter-spacing: 0.06em;
|
|
1551
|
+
color: var(--text-muted);
|
|
1552
|
+
margin: 0 0 12px 0;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
.docs-overview-server-list {
|
|
1556
|
+
list-style: none;
|
|
1557
|
+
margin: 0;
|
|
1558
|
+
padding: 0;
|
|
1559
|
+
display: flex;
|
|
1560
|
+
flex-direction: column;
|
|
1561
|
+
gap: 8px;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
.docs-overview-server {
|
|
1565
|
+
display: flex;
|
|
1566
|
+
align-items: center;
|
|
1567
|
+
flex-wrap: wrap;
|
|
1568
|
+
gap: 12px;
|
|
1569
|
+
padding: 12px 16px;
|
|
1570
|
+
border: 1px solid var(--border-color);
|
|
1571
|
+
border-radius: 10px;
|
|
1572
|
+
background: var(--surface-color);
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
.docs-overview-server-url {
|
|
1576
|
+
font-family: 'SF Mono', Monaco, Consolas, monospace;
|
|
1577
|
+
font-size: 14px;
|
|
1578
|
+
color: var(--text-color);
|
|
1579
|
+
background: var(--code-bg);
|
|
1580
|
+
padding: 4px 10px;
|
|
1581
|
+
border-radius: 6px;
|
|
1582
|
+
word-break: break-all;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
.docs-overview-server-desc {
|
|
1586
|
+
font-size: 14px;
|
|
1587
|
+
color: var(--text-muted);
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
.docs-overview-description {
|
|
1591
|
+
max-width: 820px;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1486
1594
|
/* ========== ENDPOINT SECTIONS (Redoc-style) ========== */
|
|
1487
1595
|
.endpoint-section {
|
|
1488
1596
|
padding: 32px 0;
|
|
@@ -42,7 +42,15 @@ const xUsd = spec['x-usd'] ?? {};
|
|
|
42
42
|
const { websocket: wsSpec = {}, graphql: graphqlSpec = {}, streams: streamsSpec = {}, jsonrpc: jsonrpcSpec = {}, grpc: grpcSpec = {}, tcp: tcpSpec = {}, udp: udpSpec = {} } = xUsd;
|
|
43
43
|
const docsRouteBase = String(xUsd.documentation?.routeBase ?? '').replace(/^#/, '').replace(/\/+$/, '');
|
|
44
44
|
const protocolData = detectProtocols();
|
|
45
|
-
|
|
45
|
+
// Logical priority for which protocol the docs open on (and the tab order):
|
|
46
|
+
// what a consumer of *this* API most likely came to read first. HTTP wins
|
|
47
|
+
// when present, then GraphQL, then realtime/RPC, then raw sockets.
|
|
48
|
+
const PROTOCOL_PRIORITY = ['http', 'graphql', 'websocket', 'jsonrpc', 'grpc', 'streams', 'tcp', 'udp'];
|
|
49
|
+
const protocolRank = (name) => {
|
|
50
|
+
const i = PROTOCOL_PRIORITY.indexOf(name);
|
|
51
|
+
return i === -1 ? PROTOCOL_PRIORITY.length : i;
|
|
52
|
+
};
|
|
53
|
+
const protocols = Object.keys(protocolData).sort((a, b) => protocolRank(a) - protocolRank(b));
|
|
46
54
|
let activeProtocol = protocols[0] ?? 'http';
|
|
47
55
|
let searchQuery = '';
|
|
48
56
|
let routeState = parseRouteHash();
|
|
@@ -922,9 +930,10 @@ function renderProtocolTabs() {
|
|
|
922
930
|
if (!container)
|
|
923
931
|
return;
|
|
924
932
|
container.textContent = '';
|
|
933
|
+
const isRoot = !activePagePath || activePagePath === '/';
|
|
925
934
|
for (const protocol of protocols) {
|
|
926
935
|
const button = doc.createElement('button');
|
|
927
|
-
button.className = `protocol-tab${
|
|
936
|
+
button.className = `protocol-tab${isRoot && protocol === activeProtocol ? ' active' : ''}`;
|
|
928
937
|
button.innerHTML = `${esc(protocol.charAt(0).toUpperCase() + protocol.slice(1))}${sidebarConfig.showCounts !== false ? `<span class="count">${protocolData[protocol]}</span>` : ''}`;
|
|
929
938
|
button.onclick = () => {
|
|
930
939
|
activeProtocol = protocol;
|
|
@@ -1044,7 +1053,14 @@ function renderSidebar() {
|
|
|
1044
1053
|
return;
|
|
1045
1054
|
nav.textContent = '';
|
|
1046
1055
|
renderDocsPagesNav(nav);
|
|
1047
|
-
|
|
1056
|
+
// A real doc page (or a genuine non-root 404) shows the docs nav only.
|
|
1057
|
+
// The docs root (`/`) is NOT a page — it's the overview, so it must list
|
|
1058
|
+
// the active protocol's endpoints, expanded, like the old empty-path state.
|
|
1059
|
+
const matchedPage = activePagePath
|
|
1060
|
+
? getDocsPageViews().some((p) => p.path === activePagePath)
|
|
1061
|
+
: false;
|
|
1062
|
+
const isRoot = !activePagePath || activePagePath === '/';
|
|
1063
|
+
if (matchedPage || !isRoot)
|
|
1048
1064
|
return;
|
|
1049
1065
|
const endpoints = getEndpointsForProtocol(activeProtocol).filter(endpoint => !searchQuery ||
|
|
1050
1066
|
endpoint.path.toLowerCase().includes(searchQuery) ||
|
|
@@ -1803,16 +1819,19 @@ function renderContent() {
|
|
|
1803
1819
|
scrollToActiveHeading();
|
|
1804
1820
|
return;
|
|
1805
1821
|
}
|
|
1806
|
-
|
|
1822
|
+
// The docs root (`/`) is ours to define — never a "not found". Only a
|
|
1823
|
+
// non-root path with no matching page is a genuine 404.
|
|
1824
|
+
const isRoot = !activePagePath || activePagePath === '/';
|
|
1825
|
+
if (activePagePath && !isRoot) {
|
|
1807
1826
|
renderMissingDocsPage(main);
|
|
1808
1827
|
renderToc(main);
|
|
1809
1828
|
return;
|
|
1810
1829
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
main.appendChild(
|
|
1830
|
+
// Root landing: an OpenAPI-driven overview (title, version, servers,
|
|
1831
|
+
// description) followed by the endpoint list — unless the user is
|
|
1832
|
+
// mid-search, in which case the search results take the surface.
|
|
1833
|
+
if (!searchQuery) {
|
|
1834
|
+
main.appendChild(renderDocsOverview());
|
|
1816
1835
|
}
|
|
1817
1836
|
if (searchQuery)
|
|
1818
1837
|
renderDocsSearch(main);
|
|
@@ -1829,6 +1848,67 @@ function renderContent() {
|
|
|
1829
1848
|
}
|
|
1830
1849
|
renderToc(main);
|
|
1831
1850
|
}
|
|
1851
|
+
function renderDocsOverview() {
|
|
1852
|
+
const info = (spec.info ?? {});
|
|
1853
|
+
const container = doc.createElement('div');
|
|
1854
|
+
container.className = 'docs-overview';
|
|
1855
|
+
const title = String(info.title ?? 'API');
|
|
1856
|
+
const versionBadge = info.version
|
|
1857
|
+
? `<span class="docs-overview-version">${esc(String(info.version))}</span>`
|
|
1858
|
+
: '';
|
|
1859
|
+
const header = doc.createElement('header');
|
|
1860
|
+
header.className = 'docs-overview-header';
|
|
1861
|
+
header.innerHTML = `<h1 class="docs-overview-title" id="overview">${esc(title)}${versionBadge}</h1>`;
|
|
1862
|
+
container.appendChild(header);
|
|
1863
|
+
// Contact / license line (ReDoc-style).
|
|
1864
|
+
const contact = (info.contact ?? {});
|
|
1865
|
+
const license = (info.license ?? {});
|
|
1866
|
+
const metaBits = [];
|
|
1867
|
+
if (contact.email) {
|
|
1868
|
+
const label = esc(String(contact.name ?? contact.email));
|
|
1869
|
+
metaBits.push(`E-mail: <a href="mailto:${esc(String(contact.email))}">${label}</a>`);
|
|
1870
|
+
}
|
|
1871
|
+
if (contact.url) {
|
|
1872
|
+
metaBits.push(`URL: <a href="${esc(String(contact.url))}" target="_blank" rel="noopener">${esc(String(contact.url))}</a>`);
|
|
1873
|
+
}
|
|
1874
|
+
if (license.name) {
|
|
1875
|
+
const lic = license.url
|
|
1876
|
+
? `<a href="${esc(String(license.url))}" target="_blank" rel="noopener">${esc(String(license.name))}</a>`
|
|
1877
|
+
: esc(String(license.name));
|
|
1878
|
+
metaBits.push(`License: ${lic}`);
|
|
1879
|
+
}
|
|
1880
|
+
if (metaBits.length) {
|
|
1881
|
+
const meta = doc.createElement('div');
|
|
1882
|
+
meta.className = 'docs-overview-meta';
|
|
1883
|
+
meta.innerHTML = metaBits.join('<span class="docs-overview-meta-sep">·</span>');
|
|
1884
|
+
container.appendChild(meta);
|
|
1885
|
+
}
|
|
1886
|
+
// Servers — straight from `spec.servers`.
|
|
1887
|
+
const servers = Array.isArray(spec.servers) ? spec.servers : [];
|
|
1888
|
+
if (servers.length) {
|
|
1889
|
+
const section = doc.createElement('section');
|
|
1890
|
+
section.className = 'docs-overview-servers';
|
|
1891
|
+
const rows = servers
|
|
1892
|
+
.map((s) => {
|
|
1893
|
+
const url = esc(String(s?.url ?? ''));
|
|
1894
|
+
const desc = s?.description
|
|
1895
|
+
? `<span class="docs-overview-server-desc">${esc(String(s.description))}</span>`
|
|
1896
|
+
: '';
|
|
1897
|
+
return `<li class="docs-overview-server"><code class="docs-overview-server-url">${url}</code>${desc}</li>`;
|
|
1898
|
+
})
|
|
1899
|
+
.join('');
|
|
1900
|
+
section.innerHTML = `<h2 class="docs-overview-subtitle">${servers.length > 1 ? 'Servers' : 'Server'}</h2><ul class="docs-overview-server-list">${rows}</ul>`;
|
|
1901
|
+
container.appendChild(section);
|
|
1902
|
+
}
|
|
1903
|
+
// Description (markdown).
|
|
1904
|
+
if (info.description) {
|
|
1905
|
+
const description = doc.createElement('div');
|
|
1906
|
+
description.className = 'docs-overview-description markdown-content';
|
|
1907
|
+
description.innerHTML = parseMarkdown(String(info.description));
|
|
1908
|
+
container.appendChild(description);
|
|
1909
|
+
}
|
|
1910
|
+
return container;
|
|
1911
|
+
}
|
|
1832
1912
|
function renderMissingDocsPage(main) {
|
|
1833
1913
|
const notFound = getDocsPageViews().find(page => page.path === '/404' || page.path.endsWith('/404'));
|
|
1834
1914
|
const article = doc.createElement('article');
|
|
@@ -42,7 +42,15 @@ const xUsd = spec['x-usd'] ?? {};
|
|
|
42
42
|
const { websocket: wsSpec = {}, graphql: graphqlSpec = {}, streams: streamsSpec = {}, jsonrpc: jsonrpcSpec = {}, grpc: grpcSpec = {}, tcp: tcpSpec = {}, udp: udpSpec = {} } = xUsd;
|
|
43
43
|
const docsRouteBase = String(xUsd.documentation?.routeBase ?? '').replace(/^#/, '').replace(/\/+$/, '');
|
|
44
44
|
const protocolData = detectProtocols();
|
|
45
|
-
|
|
45
|
+
// Logical priority for which protocol the docs open on (and the tab order):
|
|
46
|
+
// what a consumer of *this* API most likely came to read first. HTTP wins
|
|
47
|
+
// when present, then GraphQL, then realtime/RPC, then raw sockets.
|
|
48
|
+
const PROTOCOL_PRIORITY = ['http', 'graphql', 'websocket', 'jsonrpc', 'grpc', 'streams', 'tcp', 'udp'];
|
|
49
|
+
const protocolRank = (name) => {
|
|
50
|
+
const i = PROTOCOL_PRIORITY.indexOf(name);
|
|
51
|
+
return i === -1 ? PROTOCOL_PRIORITY.length : i;
|
|
52
|
+
};
|
|
53
|
+
const protocols = Object.keys(protocolData).sort((a, b) => protocolRank(a) - protocolRank(b));
|
|
46
54
|
let activeProtocol = protocols[0] ?? 'http';
|
|
47
55
|
let searchQuery = '';
|
|
48
56
|
let routeState = parseRouteHash();
|
|
@@ -922,9 +930,10 @@ function renderProtocolTabs() {
|
|
|
922
930
|
if (!container)
|
|
923
931
|
return;
|
|
924
932
|
container.textContent = '';
|
|
933
|
+
const isRoot = !activePagePath || activePagePath === '/';
|
|
925
934
|
for (const protocol of protocols) {
|
|
926
935
|
const button = doc.createElement('button');
|
|
927
|
-
button.className = `protocol-tab${
|
|
936
|
+
button.className = `protocol-tab${isRoot && protocol === activeProtocol ? ' active' : ''}`;
|
|
928
937
|
button.innerHTML = `${esc(protocol.charAt(0).toUpperCase() + protocol.slice(1))}${sidebarConfig.showCounts !== false ? `<span class="count">${protocolData[protocol]}</span>` : ''}`;
|
|
929
938
|
button.onclick = () => {
|
|
930
939
|
activeProtocol = protocol;
|
|
@@ -1044,7 +1053,14 @@ function renderSidebar() {
|
|
|
1044
1053
|
return;
|
|
1045
1054
|
nav.textContent = '';
|
|
1046
1055
|
renderDocsPagesNav(nav);
|
|
1047
|
-
|
|
1056
|
+
// A real doc page (or a genuine non-root 404) shows the docs nav only.
|
|
1057
|
+
// The docs root (`/`) is NOT a page — it's the overview, so it must list
|
|
1058
|
+
// the active protocol's endpoints, expanded, like the old empty-path state.
|
|
1059
|
+
const matchedPage = activePagePath
|
|
1060
|
+
? getDocsPageViews().some((p) => p.path === activePagePath)
|
|
1061
|
+
: false;
|
|
1062
|
+
const isRoot = !activePagePath || activePagePath === '/';
|
|
1063
|
+
if (matchedPage || !isRoot)
|
|
1048
1064
|
return;
|
|
1049
1065
|
const endpoints = getEndpointsForProtocol(activeProtocol).filter(endpoint => !searchQuery ||
|
|
1050
1066
|
endpoint.path.toLowerCase().includes(searchQuery) ||
|
|
@@ -1803,16 +1819,19 @@ function renderContent() {
|
|
|
1803
1819
|
scrollToActiveHeading();
|
|
1804
1820
|
return;
|
|
1805
1821
|
}
|
|
1806
|
-
|
|
1822
|
+
// The docs root (`/`) is ours to define — never a "not found". Only a
|
|
1823
|
+
// non-root path with no matching page is a genuine 404.
|
|
1824
|
+
const isRoot = !activePagePath || activePagePath === '/';
|
|
1825
|
+
if (activePagePath && !isRoot) {
|
|
1807
1826
|
renderMissingDocsPage(main);
|
|
1808
1827
|
renderToc(main);
|
|
1809
1828
|
return;
|
|
1810
1829
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
main.appendChild(
|
|
1830
|
+
// Root landing: an OpenAPI-driven overview (title, version, servers,
|
|
1831
|
+
// description) followed by the endpoint list — unless the user is
|
|
1832
|
+
// mid-search, in which case the search results take the surface.
|
|
1833
|
+
if (!searchQuery) {
|
|
1834
|
+
main.appendChild(renderDocsOverview());
|
|
1816
1835
|
}
|
|
1817
1836
|
if (searchQuery)
|
|
1818
1837
|
renderDocsSearch(main);
|
|
@@ -1829,6 +1848,67 @@ function renderContent() {
|
|
|
1829
1848
|
}
|
|
1830
1849
|
renderToc(main);
|
|
1831
1850
|
}
|
|
1851
|
+
function renderDocsOverview() {
|
|
1852
|
+
const info = (spec.info ?? {});
|
|
1853
|
+
const container = doc.createElement('div');
|
|
1854
|
+
container.className = 'docs-overview';
|
|
1855
|
+
const title = String(info.title ?? 'API');
|
|
1856
|
+
const versionBadge = info.version
|
|
1857
|
+
? `<span class="docs-overview-version">${esc(String(info.version))}</span>`
|
|
1858
|
+
: '';
|
|
1859
|
+
const header = doc.createElement('header');
|
|
1860
|
+
header.className = 'docs-overview-header';
|
|
1861
|
+
header.innerHTML = `<h1 class="docs-overview-title" id="overview">${esc(title)}${versionBadge}</h1>`;
|
|
1862
|
+
container.appendChild(header);
|
|
1863
|
+
// Contact / license line (ReDoc-style).
|
|
1864
|
+
const contact = (info.contact ?? {});
|
|
1865
|
+
const license = (info.license ?? {});
|
|
1866
|
+
const metaBits = [];
|
|
1867
|
+
if (contact.email) {
|
|
1868
|
+
const label = esc(String(contact.name ?? contact.email));
|
|
1869
|
+
metaBits.push(`E-mail: <a href="mailto:${esc(String(contact.email))}">${label}</a>`);
|
|
1870
|
+
}
|
|
1871
|
+
if (contact.url) {
|
|
1872
|
+
metaBits.push(`URL: <a href="${esc(String(contact.url))}" target="_blank" rel="noopener">${esc(String(contact.url))}</a>`);
|
|
1873
|
+
}
|
|
1874
|
+
if (license.name) {
|
|
1875
|
+
const lic = license.url
|
|
1876
|
+
? `<a href="${esc(String(license.url))}" target="_blank" rel="noopener">${esc(String(license.name))}</a>`
|
|
1877
|
+
: esc(String(license.name));
|
|
1878
|
+
metaBits.push(`License: ${lic}`);
|
|
1879
|
+
}
|
|
1880
|
+
if (metaBits.length) {
|
|
1881
|
+
const meta = doc.createElement('div');
|
|
1882
|
+
meta.className = 'docs-overview-meta';
|
|
1883
|
+
meta.innerHTML = metaBits.join('<span class="docs-overview-meta-sep">·</span>');
|
|
1884
|
+
container.appendChild(meta);
|
|
1885
|
+
}
|
|
1886
|
+
// Servers — straight from `spec.servers`.
|
|
1887
|
+
const servers = Array.isArray(spec.servers) ? spec.servers : [];
|
|
1888
|
+
if (servers.length) {
|
|
1889
|
+
const section = doc.createElement('section');
|
|
1890
|
+
section.className = 'docs-overview-servers';
|
|
1891
|
+
const rows = servers
|
|
1892
|
+
.map((s) => {
|
|
1893
|
+
const url = esc(String(s?.url ?? ''));
|
|
1894
|
+
const desc = s?.description
|
|
1895
|
+
? `<span class="docs-overview-server-desc">${esc(String(s.description))}</span>`
|
|
1896
|
+
: '';
|
|
1897
|
+
return `<li class="docs-overview-server"><code class="docs-overview-server-url">${url}</code>${desc}</li>`;
|
|
1898
|
+
})
|
|
1899
|
+
.join('');
|
|
1900
|
+
section.innerHTML = `<h2 class="docs-overview-subtitle">${servers.length > 1 ? 'Servers' : 'Server'}</h2><ul class="docs-overview-server-list">${rows}</ul>`;
|
|
1901
|
+
container.appendChild(section);
|
|
1902
|
+
}
|
|
1903
|
+
// Description (markdown).
|
|
1904
|
+
if (info.description) {
|
|
1905
|
+
const description = doc.createElement('div');
|
|
1906
|
+
description.className = 'docs-overview-description markdown-content';
|
|
1907
|
+
description.innerHTML = parseMarkdown(String(info.description));
|
|
1908
|
+
container.appendChild(description);
|
|
1909
|
+
}
|
|
1910
|
+
return container;
|
|
1911
|
+
}
|
|
1832
1912
|
function renderMissingDocsPage(main) {
|
|
1833
1913
|
const notFound = getDocsPageViews().find(page => page.path === '/404' || page.path.endsWith('/404'));
|
|
1834
1914
|
const article = doc.createElement('article');
|