Skip to content

Commit cfd12e0

Browse files
committed
docs(moonapi): add gh-pages API reference site generated from source doc-comments.
Signed-off-by: 林晨 (Leo Cheng) <chengkelfan@qq.com>
1 parent 71eb366 commit cfd12e0

2 files changed

Lines changed: 382 additions & 0 deletions

File tree

docs/index.html

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>moonapi — MoonBit web framework API</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600&display=swap" rel="stylesheet"><style>
2+
:root{
3+
--bg:#fbfbfd; --panel:#ffffff; --panel-2:#f6f7fb; --ink:#14181f;
4+
--muted:#5b6675; --line:#e8ebf1; --accent:#6d5efc; --accent-soft:#efecff; --out:#0ca678;
5+
--code-bg:#f4f5f9; --shadow:0 1px 2px rgba(20,24,31,.04),0 8px 24px -12px rgba(20,24,31,.10);
6+
}
7+
@media (prefers-color-scheme:dark){:root{
8+
--bg:#0b0e14; --panel:#131722; --panel-2:#0f131c; --ink:#e9edf6; --muted:#96a1b5;
9+
--line:#212736; --accent:#9d8bff; --accent-soft:#1c1b3a; --out:#2dd4a7;
10+
--code-bg:#161b26; --shadow:0 1px 2px rgba(0,0,0,.3),0 12px 30px -14px rgba(0,0,0,.5);
11+
}}
12+
:root[data-theme=light]{--bg:#fbfbfd;--panel:#fff;--panel-2:#f6f7fb;--ink:#14181f;--muted:#5b6675;--line:#e8ebf1;--accent:#6d5efc;--accent-soft:#efecff;--out:#0ca678;--code-bg:#f4f5f9;--shadow:0 1px 2px rgba(20,24,31,.04),0 8px 24px -12px rgba(20,24,31,.10)}
13+
:root[data-theme=dark]{--bg:#0b0e14;--panel:#131722;--panel-2:#0f131c;--ink:#e9edf6;--muted:#96a1b5;--line:#212736;--accent:#9d8bff;--accent-soft:#1c1b3a;--out:#2dd4a7;--code-bg:#161b26;--shadow:0 1px 2px rgba(0,0,0,.3),0 12px 30px -14px rgba(0,0,0,.5)}
14+
*{box-sizing:border-box}
15+
html{scroll-behavior:smooth}
16+
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}*{animation:none!important;transition:none!important}}
17+
body{margin:0;background:var(--bg);color:var(--ink);
18+
font-family:"IBM Plex Sans",system-ui,-apple-system,Segoe UI,Roboto,sans-serif;
19+
font-size:15.5px;line-height:1.6;-webkit-font-smoothing:antialiased}
20+
code,pre,.mono{font-family:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
21+
a{color:var(--accent);text-decoration:none}
22+
a:hover{text-decoration:underline}
23+
.layout{display:grid;grid-template-columns:264px minmax(0,1fr);max-width:1180px;margin:0 auto}
24+
.sidebar{position:sticky;top:0;align-self:start;height:100vh;overflow-y:auto;
25+
border-right:1px solid var(--line);padding:1.6rem 1.1rem 2rem;background:var(--panel-2)}
26+
.brand{display:flex;align-items:center;gap:.55rem;font-family:"IBM Plex Mono";font-weight:600;
27+
font-size:1.35rem;letter-spacing:-.01em;color:var(--ink);margin-bottom:.15rem}
28+
.brand .dot{width:11px;height:11px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}
29+
.brand-sub{color:var(--muted);font-size:.8rem;margin:0 0 1.3rem;padding-left:.15rem}
30+
.side-nav{display:flex;flex-direction:column;gap:.1rem}
31+
.side-nav a{color:var(--muted);font-size:.9rem;padding:.32rem .6rem;border-radius:8px;
32+
font-family:"IBM Plex Mono";display:flex;align-items:center;gap:.4rem;border-left:2px solid transparent}
33+
.side-nav a .at{color:var(--accent);opacity:.6}
34+
.side-nav a:hover{background:var(--accent-soft);color:var(--ink);text-decoration:none}
35+
.side-nav a.active{color:var(--ink);background:var(--accent-soft);border-left-color:var(--accent);font-weight:500}
36+
.side-nav a.active .at{opacity:1}
37+
.side-foot{margin-top:1.6rem;padding-top:1.1rem;border-top:1px solid var(--line);display:flex;flex-wrap:wrap;gap:.4rem}
38+
.side-foot img{height:20px;display:block}
39+
.theme-btn{margin-top:1rem;background:none;border:1px solid var(--line);color:var(--muted);
40+
border-radius:8px;padding:.35rem .6rem;font:inherit;font-size:.82rem;cursor:pointer;width:100%}
41+
.theme-btn:hover{border-color:var(--accent);color:var(--ink)}
42+
main{padding:2.6rem 2.4rem 5rem;min-width:0}
43+
.hero h1{font-family:"IBM Plex Mono";font-weight:600;font-size:2.9rem;letter-spacing:-.02em;margin:0}
44+
.hero .tag{color:var(--muted);font-size:1.12rem;max-width:62ch;margin:.5rem 0 1.1rem;text-wrap:balance}
45+
.badges{display:flex;flex-wrap:wrap;gap:.45rem;margin:0 0 1.4rem}
46+
.badges img{height:21px;display:block}
47+
.install{display:flex;align-items:center;gap:.6rem;background:var(--panel);border:1px solid var(--line);
48+
border-radius:12px;padding:.65rem 1rem;box-shadow:var(--shadow);max-width:420px}
49+
.install .prompt{color:var(--out);user-select:none;font-weight:600}
50+
.install code{flex:1;font-size:.95rem}
51+
.copy{background:none;border:1px solid var(--line);border-radius:7px;color:var(--muted);
52+
cursor:pointer;font:inherit;font-size:.72rem;padding:.2rem .5rem}
53+
.copy:hover{border-color:var(--accent);color:var(--accent)}
54+
.copy.ok{color:var(--out);border-color:var(--out)}
55+
.contract{margin:2.1rem 0 .5rem;background:
56+
radial-gradient(120% 130% at 100% 0%, var(--accent-soft) 0%, transparent 55%), var(--panel);
57+
border:1px solid var(--line);border-radius:16px;padding:1.2rem 1.4rem;box-shadow:var(--shadow)}
58+
.contract h2{margin:0 0 .6rem;font-size:1.06rem;display:flex;align-items:center;gap:.5rem}
59+
.contract h2 .spark{color:var(--accent)}
60+
.contract pre{margin:0;overflow-x:auto;font-size:.92rem;line-height:1.7}
61+
.contract .k{color:#8b5cf6;font-weight:500}.contract .ty{color:var(--accent)}.contract .op{color:var(--muted)}
62+
section.pkg{scroll-margin-top:1.2rem;padding-top:2.4rem;margin-top:2rem;border-top:1px solid var(--line)}
63+
section.pkg > h2{font-family:"IBM Plex Mono";font-size:1.55rem;margin:0 0 .15rem;letter-spacing:-.01em}
64+
section.pkg > h2 .at{color:var(--accent)}
65+
.pdesc{color:var(--muted);margin:.15rem 0 1.2rem;max-width:72ch}
66+
.item{background:var(--panel);border:1px solid var(--line);border-radius:13px;
67+
padding:1rem 1.2rem;margin:.85rem 0;box-shadow:var(--shadow);transition:border-color .15s,transform .15s}
68+
.item:hover{border-color:color-mix(in oklab,var(--accent) 40%,var(--line))}
69+
.kind{display:inline-block;font-size:.66rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;
70+
border-radius:6px;padding:.1rem .45rem;margin-bottom:.55rem;
71+
color:var(--accent);background:var(--accent-soft);border:1px solid color-mix(in oklab,var(--accent) 26%,transparent)}
72+
.item[data-k=struct] .kind{--c:#8b5cf6}.item[data-k=fn] .kind{--c:#0ca678}.item[data-k=let] .kind{--c:#2563eb}
73+
.item[data-k=enum] .kind{--c:#d6336c}.item[data-k=type] .kind{--c:#0891b2}
74+
.item .kind{color:var(--c,var(--accent));background:color-mix(in oklab,var(--c,var(--accent)) 13%,transparent);
75+
border-color:color-mix(in oklab,var(--c,var(--accent)) 30%,transparent)}
76+
.sig{font-size:.98rem;margin:0 0 .55rem;overflow-x:auto;white-space:pre;color:var(--ink);padding-bottom:.15rem}
77+
.sig .k{color:#8b5cf6;font-weight:500}.sig .ty{color:var(--accent)}.sig .op{color:var(--muted)}
78+
@media (prefers-color-scheme:dark){.sig .k,.contract .k{color:#b794ff}}
79+
.doc{margin:0;color:var(--ink);max-width:76ch}
80+
.doc code{background:var(--code-bg);padding:.06rem .35rem;border-radius:5px;font-size:.9em;color:var(--accent)}
81+
footer{margin-top:3rem;padding-top:1.3rem;border-top:1px solid var(--line);color:var(--muted);font-size:.9rem}
82+
@media (max-width:820px){
83+
.layout{grid-template-columns:1fr}
84+
.sidebar{position:static;height:auto;border-right:none;border-bottom:1px solid var(--line)}
85+
.side-nav{flex-flow:row wrap}.side-nav a{border-left:none}.side-nav a.active{border-left:none}
86+
main{padding:1.8rem 1.2rem 4rem}.hero h1{font-size:2.2rem}
87+
}
88+
</style></head><body>
89+
<div class="layout">
90+
<aside class="sidebar"><div class="brand"><span class="dot"></span>moonapi</div><p class="brand-sub">MoonBit web framework — API reference</p><nav class="side-nav">
91+
<a href="#app"><span class="at">§</span>Application & routing</a>
92+
<a href="#openapi"><span class="at">§</span>OpenAPI & Swagger</a>
93+
</nav><button class="theme-btn" id="theme">◐ toggle theme</button><div class="side-foot"><a href="https://github.com/Lfan-ke/moonapi/actions"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/Lfan-ke/moonapi/ci.yml?branch=master&label=CI&logo=github"></a><a href="https://mooncakes.io/docs/Lfan-ke/moonapi"><img alt="mooncakes" src="https://img.shields.io/badge/mooncakes-Lfan--ke%2Fmoonapi-1f6feb"></a></div></aside>
94+
<main><header class="hero"><h1>moonapi</h1><p class="tag">A typed web framework for MoonBit &#8212; FastAPI-style routing and multi-version OpenAPI, on the moonasgi SEAM. Backend-agnostic &#8212; the async transport lives in the server (mooncat) that runs the app.</p><div class="badges"><a href="https://github.com/Lfan-ke/moonapi/actions"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/Lfan-ke/moonapi/ci.yml?branch=master&label=CI&logo=github"></a><img alt="tests" src="https://img.shields.io/badge/tests-3%20passing%20%C3%974%20backends-0ca678"><a href="https://github.com/Lfan-ke/moonapi"><img alt="GitHub" src="https://img.shields.io/badge/GitHub-source-24292f?logo=github"></a><img alt="license" src="https://img.shields.io/badge/license-Apache--2.0-6d5efc"></div><div class="install"><span class="prompt">$</span><code>moon add Lfan-ke/moonapi</code><button class="copy" data-copy="moon add Lfan-ke/moonapi">copy</button></div><div class="contract"><h2><span class="spark">&#10038;</span> The contract at a glance</h2><pre><span class="k">let</span> app = <span class="ty">App</span>::new()
95+
app.get(&quot;/users/:id&quot;, ctx =&gt; text(200, &quot;user &quot; + ctx.param(&quot;id&quot;).unwrap()))
96+
97+
<span class="k">let</span> spec = app.openapi_json(version=<span class="ty">OpenApi31</span>) // also <span class="ty">OpenApi30</span> / <span class="ty">Swagger20</span>
98+
@mooncat.serve(app.to_asgi(), port=8000) // run it (native)</pre></div></header>
99+
<section class="pkg" id="app"><h2><span class="at">§</span>Application & routing</h2><p class="pdesc">The App, its route builders (get / post / ...), :param path matching, the Context, and text / json response helpers - plus App::to_asgi to run anywhere.</p>
100+
<div class="item" data-k="enum"><span class="kind">enum</span><pre class="sig"><span class="k">enum</span> <span class="ty">Method</span></pre><p class="doc">HTTP methods a moonapi route can bind to.</p></div>
101+
<div class="item" data-k="struct"><span class="kind">struct</span><pre class="sig"><span class="k">struct</span> <span class="ty">Context</span></pre><p class="doc">The per-request context handed to a handler: the raw request plus the path parameters extracted from the matched route (<code>:name</code> segments).</p></div>
102+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Context</span>::param(self : <span class="ty">Context</span>, name : <span class="ty">String</span>) <span class="op">-&gt;</span> <span class="ty">String</span><span class="op">?</span></pre><p class="doc">Look up a path parameter by name.</p></div>
103+
<div class="item" data-k="type"><span class="kind">type</span><pre class="sig"><span class="k">type</span> <span class="ty">ApiHandler</span> = (<span class="ty">Context</span>) <span class="op">-&gt;</span> @moonasgi.<span class="ty">Response</span></pre><p class="doc">A moonapi route handler: request context in, response out.</p></div>
104+
<div class="item" data-k="struct"><span class="kind">struct</span><pre class="sig"><span class="k">struct</span> <span class="ty">App</span></pre><p class="doc">A moonapi application: an ordered set of routes that compiles to a moonasgi <code>AsgiApp</code> any server (mooncat) can run.</p></div>
105+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::new() <span class="op">-&gt;</span> <span class="ty">App</span></pre><p class="doc">Create an empty application.</p></div>
106+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::route( self : <span class="ty">App</span>, verb : <span class="ty">Method</span>, path : <span class="ty">String</span>, handler : <span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = &quot;&quot;) <span class="op">-&gt;</span> <span class="ty">Unit</span></pre><p class="doc">Register a route for an explicit method.</p></div>
107+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::get( self : <span class="ty">App</span>, path : <span class="ty">String</span>, handler : <span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = &quot;&quot;) <span class="op">-&gt;</span> <span class="ty">Unit</span></pre></div>
108+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::post( self : <span class="ty">App</span>, path : <span class="ty">String</span>, handler : <span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = &quot;&quot;) <span class="op">-&gt;</span> <span class="ty">Unit</span></pre></div>
109+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::put( self : <span class="ty">App</span>, path : <span class="ty">String</span>, handler : <span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = &quot;&quot;) <span class="op">-&gt;</span> <span class="ty">Unit</span></pre></div>
110+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::patch( self : <span class="ty">App</span>, path : <span class="ty">String</span>, handler : <span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = &quot;&quot;) <span class="op">-&gt;</span> <span class="ty">Unit</span></pre></div>
111+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::delete( self : <span class="ty">App</span>, path : <span class="ty">String</span>, handler : <span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = &quot;&quot;) <span class="op">-&gt;</span> <span class="ty">Unit</span></pre></div>
112+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> text(status : <span class="ty">Int</span>, body : <span class="ty">String</span>) <span class="op">-&gt;</span> @moonasgi.<span class="ty">Response</span></pre><p class="doc">A plain-text response.</p></div>
113+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> json(status : <span class="ty">Int</span>, value : <span class="ty">Json</span>) <span class="op">-&gt;</span> @moonasgi.<span class="ty">Response</span></pre><p class="doc">A JSON response serialised from a <code>Json</code> value.</p></div>
114+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::handle( self : <span class="ty">App</span>, request : @moonasgi.<span class="ty">Request</span>) <span class="op">-&gt;</span> @moonasgi.<span class="ty">Response</span></pre><p class="doc">Route a request to its handler, returning 404 when no path matches and 405 when a path matches but no method does.</p></div>
115+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::to_asgi(self : <span class="ty">App</span>) <span class="op">-&gt;</span> @moonasgi.<span class="ty">AsgiApp</span></pre><p class="doc">Compile the app to a moonasgi <code>AsgiApp</code> a server can run: drain the request body, route it, and stream the response back over the SEAM.</p></div>
116+
</section>
117+
<section class="pkg" id="openapi"><h2><span class="at">§</span>OpenAPI & Swagger</h2><p class="pdesc">Multi-version OpenAPI / Swagger generation (2.0 / 3.0 / 3.1) from the same routes, and a ready-to-serve Swagger UI page.</p>
118+
<div class="item" data-k="enum"><span class="kind">enum</span><pre class="sig"><span class="k">enum</span> <span class="ty">OpenApiVersion</span></pre><p class="doc">Target OpenAPI / Swagger document version. moonapi emits every mainstream version from the same route descriptors — a &quot;good FastAPI&quot; is not pinned to one spec version.</p></div>
119+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::openapi( self : <span class="ty">App</span>, version<span class="op">?</span> : <span class="ty">OpenApiVersion</span> = <span class="ty">OpenApi31</span>, title<span class="op">?</span> : <span class="ty">String</span> = &quot;moonapi&quot;, api_version<span class="op">?</span> : <span class="ty">String</span> = &quot;0.1.0&quot;) <span class="op">-&gt;</span> <span class="ty">Json</span></pre><p class="doc">Build the OpenAPI / Swagger document for the app as a <code>Json</code> value, walking the registered routes once into paths → methods → operations.</p></div>
120+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">App</span>::openapi_json( self : <span class="ty">App</span>, version<span class="op">?</span> : <span class="ty">OpenApiVersion</span> = <span class="ty">OpenApi31</span>) <span class="op">-&gt;</span> <span class="ty">String</span></pre><p class="doc">The app&#x27;s OpenAPI / Swagger document for <code>version</code>, stringified.</p></div>
121+
<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> swagger_ui( spec_url<span class="op">?</span> : <span class="ty">String</span> = &quot;/openapi.json&quot;, title<span class="op">?</span> : <span class="ty">String</span> = &quot;moonapi&quot;) <span class="op">-&gt;</span> <span class="ty">String</span></pre><p class="doc">A self-contained Swagger UI page rendering the document served at <code>spec_url</code>.</p></div>
122+
</section>
123+
<footer>Generated from source <code>///</code> doc-comments · <a href="https://mooncakes.io/docs/Lfan-ke/moonapi">mooncakes</a> · <a href="https://github.com/Lfan-ke/moonapi">GitHub</a> · Apache-2.0 &#169; Leo Cheng</footer>
124+
</main></div><script>
125+
document.addEventListener("DOMContentLoaded",()=>{
126+
document.querySelectorAll("[data-copy]").forEach(btn=>btn.addEventListener("click",()=>{
127+
navigator.clipboard.writeText(btn.getAttribute("data-copy")).then(()=>{
128+
const t=btn.textContent;btn.textContent="copied";btn.classList.add("ok");
129+
setTimeout(()=>{btn.textContent=t;btn.classList.remove("ok");},1100);});}));
130+
const links=[...document.querySelectorAll(".side-nav a")];
131+
const map=Object.fromEntries(links.map(a=>[a.getAttribute("href").slice(1),a]));
132+
const spy=new IntersectionObserver(es=>{es.forEach(e=>{if(e.isIntersecting){
133+
links.forEach(a=>a.classList.remove("active"));const a=map[e.target.id];if(a)a.classList.add("active");}});},
134+
{rootMargin:"-10% 0px -80% 0px"});
135+
document.querySelectorAll("section.pkg").forEach(s=>spy.observe(s));
136+
const tb=document.getElementById("theme");if(tb)tb.addEventListener("click",()=>{
137+
const cur=document.documentElement.getAttribute("data-theme")
138+
||(matchMedia("(prefers-color-scheme:dark)").matches?"dark":"light");
139+
document.documentElement.setAttribute("data-theme",cur==="dark"?"light":"dark");});
140+
});
141+
</script></body></html>

0 commit comments

Comments
 (0)