|
89 | 89 | <div class="layout"> |
90 | 90 | <aside class="sidebar"><div class="brand"><span class="dot"></span>moonzero</div><p class="brand-sub">MoonBit service framework — API reference</p><nav class="side-nav"> |
91 | 91 | <a href="#service"><span class="at">§</span>Service assembly</a> |
| 92 | +<a href="#middleware"><span class="at">§</span>Middleware set</a> |
| 93 | +<a href="#group"><span class="at">§</span>Route groups</a> |
92 | 94 | </nav><button class="theme-btn" id="theme">◐ toggle theme</button><div class="side-foot"><a href="https://github.com/Lfan-ke/moonzero/actions"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/Lfan-ke/moonzero/ci.yml?branch=master&label=CI&logo=github"></a><a href="https://mooncakes.io/docs/Lfan-ke/moonzero"><img alt="mooncakes" src="https://img.shields.io/badge/mooncakes-Lfan--ke%2Fmoonzero-1f6feb"></a></div></aside> |
93 | | -<main><header class="hero"><h1>moonzero</h1><p class="tag">A service framework for MoonBit — config-driven assembly of a moonapi app with middleware into a runnable AsgiApp, the way go-zero does for Go. Backend-agnostic; served by mooncat.</p><div class="badges"><a href="https://github.com/Lfan-ke/moonzero/actions"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/Lfan-ke/moonzero/ci.yml?branch=master&label=CI&logo=github"></a><img alt="tests" src="https://img.shields.io/badge/tests-2%20passing%20%C3%974%20backends-0ca678"><a href="https://github.com/Lfan-ke/moonzero"><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/moonzero</code><button class="copy" data-copy="moon add Lfan-ke/moonzero">copy</button></div><div class="contract"><h2><span class="spark">✶</span> The contract at a glance</h2><pre><span class="k">let</span> conf = <span class="ty">ServiceConf</span>::new(name="greet", port=8888) |
| 95 | +<main><header class="hero"><h1>moonzero</h1><p class="tag">A service framework for MoonBit — config-driven assembly of a moonapi app with middleware into a runnable AsgiApp, the way go-zero does for Go. Backend-agnostic; served by mooncat.</p><div class="badges"><a href="https://github.com/Lfan-ke/moonzero/actions"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/Lfan-ke/moonzero/ci.yml?branch=master&label=CI&logo=github"></a><img alt="tests" src="https://img.shields.io/badge/tests-12%20passing%20%C3%974%20backends-0ca678"><a href="https://github.com/Lfan-ke/moonzero"><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/moonzero</code><button class="copy" data-copy="moon add Lfan-ke/moonzero">copy</button></div><div class="contract"><h2><span class="spark">✶</span> The contract at a glance</h2><pre><span class="k">let</span> conf = <span class="ty">ServiceConf</span>::new(name="greet", port=8888) |
94 | 96 | <span class="k">let</span> server = <span class="ty">Server</span>::new(conf, app).use_(logging) |
95 | 97 |
|
96 | 98 | server.describe() // "greet listening on 0.0.0.0:8888" |
97 | 99 | @mooncat.serve(server.to_asgi(), port=conf.port) // run it (native)</pre></div></header> |
98 | | -<section class="pkg" id="service"><h2><span class="at">§</span>Service assembly</h2><p class="pdesc">ServiceConf + Server tie a moonapi App and a middleware onion into a runnable AsgiApp; logging is a built-in middleware. Served by mooncat.</p> |
99 | | -<div class="item" data-k="struct"><span class="kind">struct</span><pre class="sig"><span class="k">struct</span> <span class="ty">ServiceConf</span></pre><p class="doc">Service configuration (← go-zero's <code>ServiceConf</code>): a name and a bind address.</p></div> |
100 | | -<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">ServiceConf</span>::new( name<span class="op">?</span> : <span class="ty">String</span> = "app", host<span class="op">?</span> : <span class="ty">String</span> = "0.0.0.0", port<span class="op">?</span> : <span class="ty">Int</span> = 8888) <span class="op">-></span> <span class="ty">ServiceConf</span></pre><p class="doc">Build a config with sensible defaults (<code>0.0.0.0:8888</code>).</p></div> |
| 100 | +<section class="pkg" id="service"><h2><span class="at">§</span>Service assembly</h2><p class="pdesc">ServiceConf (typed config: name, host, port, timeout, log level) + Server tie a moonapi App and a middleware onion into a runnable AsgiApp; logging is a built-in middleware. Served by mooncat.</p> |
| 101 | +<div class="item" data-k="enum"><span class="kind">enum</span><pre class="sig"><span class="k">enum</span> <span class="ty">LogLevel</span></pre><p class="doc">Log verbosity (← go-zero's <code>LogConf.Level</code>), ordered from most to least verbose. <code>Compare</code> follows that order so thresholds can be tested directly.</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">LogLevel</span>::to_string(self : <span class="ty">LogLevel</span>) <span class="op">-></span> <span class="ty">String</span></pre><p class="doc">The canonical lowercase name go-zero uses on the wire.</p></div> |
| 103 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">LogLevel</span>::parse(s : <span class="ty">String</span>) <span class="op">-></span> <span class="ty">LogLevel</span></pre><p class="doc">Parse a level name, falling back to <code>Info</code> for anything unrecognised — the same lenient default go-zero applies to a missing/empty level.</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">ServiceConf</span></pre><p class="doc">Service configuration (← go-zero's <code>ServiceConf</code>): the service name, its bind address, a request timeout, and the log level. <code>timeout_ms</code> is the per-request budget in milliseconds; <code>0</code> disables the deadline.</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">ServiceConf</span>::new( name<span class="op">?</span> : <span class="ty">String</span> = "app", host<span class="op">?</span> : <span class="ty">String</span> = "0.0.0.0", port<span class="op">?</span> : <span class="ty">Int</span> = 8888, timeout_ms<span class="op">?</span> : <span class="ty">Int</span> = 3000, log_level<span class="op">?</span> : <span class="ty">LogLevel</span> = <span class="ty">Info</span>) <span class="op">-></span> <span class="ty">ServiceConf</span></pre><p class="doc">Build a config with sensible defaults (<code>0.0.0.0:8888</code>, 3s timeout, <code>info</code>).</p></div> |
101 | 106 | <div class="item" data-k="type"><span class="kind">type</span><pre class="sig"><span class="k">type</span> <span class="ty">Middleware</span> = (@moonasgi.<span class="ty">AsgiApp</span>) <span class="op">-></span> @moonasgi.<span class="ty">AsgiApp</span></pre><p class="doc">An AsgiApp transformer — one layer of the middleware onion.</p></div> |
102 | 107 | <div class="item" data-k="struct"><span class="kind">struct</span><pre class="sig"><span class="k">struct</span> <span class="ty">Server</span></pre><p class="doc">A moonzero service: its config plus the assembled application (a moonapi App with any middleware already wrapped around it).</p></div> |
103 | 108 | <div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Server</span>::new(conf : <span class="ty">ServiceConf</span>, app : @moonapi.<span class="ty">App</span>) <span class="op">-></span> <span class="ty">Server</span></pre><p class="doc">Assemble a service from config and a moonapi application.</p></div> |
|
106 | 111 | <div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Server</span>::describe(self : <span class="ty">Server</span>) <span class="op">-></span> <span class="ty">String</span></pre><p class="doc">A human-readable description of what this service binds to.</p></div> |
107 | 112 | <div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> logging(inner : @moonasgi.<span class="ty">AsgiApp</span>) <span class="op">-></span> @moonasgi.<span class="ty">AsgiApp</span></pre><p class="doc">A request-logging middleware: prints <code>METHOD path</code> for each HTTP request, then delegates to the wrapped application.</p></div> |
108 | 113 | </section> |
| 114 | +<section class="pkg" id="middleware"><h2><span class="at">§</span>Middleware set</h2><p class="pdesc">The onion layers that wrap the app: recovery (500 instead of a panic), cors (Access-Control-* headers), and request_id (x-request-id per request).</p> |
| 115 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> recovery(inner : @moonasgi.<span class="ty">AsgiApp</span>) <span class="op">-></span> @moonasgi.<span class="ty">AsgiApp</span></pre><p class="doc">Recovery middleware (← go-zero's <code>RecoverHandler</code>): run the wrapped application inside a <code>try</code>, and if it raises, emit a <code>500 Internal Server Error</code> instead of letting the failure escape to the server. A downstream that has already streamed its response start before raising will produce a second start event; recovery is a last-resort guard, so it always answers rather than trying to detect that race.</p></div> |
| 116 | +<div class="item" data-k="struct"><span class="kind">struct</span><pre class="sig"><span class="k">struct</span> <span class="ty">CorsConf</span></pre><p class="doc">CORS configuration (← go-zero's <code>cors.Middleware</code> options): the values echoed back in the <code>Access-Control-*</code> preflight/response headers.</p></div> |
| 117 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">CorsConf</span>::new( allow_origin<span class="op">?</span> : <span class="ty">String</span> = "*", allow_methods<span class="op">?</span> : <span class="ty">String</span> = "<span class="ty">GET</span>, <span class="ty">POST</span>, <span class="ty">PUT</span>, <span class="ty">PATCH</span>, <span class="ty">DELETE</span>, <span class="ty">HEAD</span>, <span class="ty">OPTIONS</span>", allow_headers<span class="op">?</span> : <span class="ty">String</span> = "<span class="ty">Content</span>-<span class="ty">Type</span>, <span class="ty">Authorization</span>", allow_credentials<span class="op">?</span> : <span class="ty">Bool</span> = false, max_age<span class="op">?</span> : <span class="ty">Int</span> = 86400) <span class="op">-></span> <span class="ty">CorsConf</span></pre><p class="doc">Build a permissive CORS config: any origin, the full method set, and a one-day preflight cache. Credentials are off by default, matching go-zero.</p></div> |
| 118 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> cors(conf : <span class="ty">CorsConf</span>) <span class="op">-></span> <span class="ty">Middleware</span></pre><p class="doc">CORS middleware (← go-zero's <code>cors.Middleware</code>): wrap the outbound <code>Send</code> so the configured <code>Access-Control-*</code> headers are injected onto every <code>HttpResponseStart</code>, leaving the body and other events untouched.</p></div> |
| 119 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> request_id(header<span class="op">?</span> : <span class="ty">String</span> = "x-request-id") <span class="op">-></span> <span class="ty">Middleware</span></pre><p class="doc">Request-ID middleware (← go-zero's trace/<code>x-request-id</code> handling): reuse an inbound <code>x-request-id</code> if the client sent one, otherwise mint a fresh monotonic id, and stamp it onto every response's <code>HttpResponseStart</code>. The counter is captured once per assembly, so ids stay unique across the requests this layer serves.</p></div> |
| 120 | +</section> |
| 121 | +<section class="pkg" id="group"><h2><span class="at">§</span>Route groups</h2><p class="pdesc">Group registers a set of moonapi routes under a shared path prefix, so related endpoints are declared without repeating the prefix.</p> |
| 122 | +<div class="item" data-k="struct"><span class="kind">struct</span><pre class="sig"><span class="k">struct</span> <span class="ty">Group</span></pre><p class="doc">A route group (← go-zero's <code>RouteGroup</code>): registers a set of routes on an underlying <code>moonapi.App</code> under a shared path prefix, so related endpoints (e.g. everything under <code>/api/v1</code>) are declared without repeating the prefix.</p></div> |
| 123 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Group</span>::new(app : @moonapi.<span class="ty">App</span>, prefix : <span class="ty">String</span>) <span class="op">-></span> <span class="ty">Group</span></pre><p class="doc">Open a group that prefixes every route it registers with <code>prefix</code> on <code>app</code>.</p></div> |
| 124 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Group</span>::prefix(self : <span class="ty">Group</span>) <span class="op">-></span> <span class="ty">String</span></pre><p class="doc">The prefix this group joins onto each registered route.</p></div> |
| 125 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Group</span>::route( self : <span class="ty">Group</span>, verb : @moonapi.<span class="ty">Method</span>, path : <span class="ty">String</span>, handler : @moonapi.<span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = "") <span class="op">-></span> <span class="ty">Unit</span></pre><p class="doc">Register a route for an explicit method under the group's prefix.</p></div> |
| 126 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Group</span>::get( self : <span class="ty">Group</span>, path : <span class="ty">String</span>, handler : @moonapi.<span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = "") <span class="op">-></span> <span class="ty">Unit</span></pre><p class="doc">Register a <code>GET</code> route under the group's prefix.</p></div> |
| 127 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Group</span>::post( self : <span class="ty">Group</span>, path : <span class="ty">String</span>, handler : @moonapi.<span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = "") <span class="op">-></span> <span class="ty">Unit</span></pre><p class="doc">Register a <code>POST</code> route under the group's prefix.</p></div> |
| 128 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Group</span>::put( self : <span class="ty">Group</span>, path : <span class="ty">String</span>, handler : @moonapi.<span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = "") <span class="op">-></span> <span class="ty">Unit</span></pre><p class="doc">Register a <code>PUT</code> route under the group's prefix.</p></div> |
| 129 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Group</span>::patch( self : <span class="ty">Group</span>, path : <span class="ty">String</span>, handler : @moonapi.<span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = "") <span class="op">-></span> <span class="ty">Unit</span></pre><p class="doc">Register a <code>PATCH</code> route under the group's prefix.</p></div> |
| 130 | +<div class="item" data-k="fn"><span class="kind">fn</span><pre class="sig"><span class="k">fn</span> <span class="ty">Group</span>::delete( self : <span class="ty">Group</span>, path : <span class="ty">String</span>, handler : @moonapi.<span class="ty">ApiHandler</span>, summary<span class="op">?</span> : <span class="ty">String</span> = "") <span class="op">-></span> <span class="ty">Unit</span></pre><p class="doc">Register a <code>DELETE</code> route under the group's prefix.</p></div> |
| 131 | +</section> |
109 | 132 | <footer>Generated from source <code>///</code> doc-comments · <a href="https://mooncakes.io/docs/Lfan-ke/moonzero">mooncakes</a> · <a href="https://github.com/Lfan-ke/moonzero">GitHub</a> · Apache-2.0 © Leo Cheng</footer> |
110 | 133 | </main></div><script> |
111 | 134 | document.addEventListener("DOMContentLoaded",()=>{ |
|
0 commit comments