From 184f361edcd686baffea6ff4ff660f93455e338f Mon Sep 17 00:00:00 2001 From: Beau Barker Date: Fri, 2 Aug 2024 11:14:34 +1000 Subject: [PATCH 1/2] Add docs back --- docs/Async.md | 41 +++++++++++++++++++++++++ docs/Dispatch.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ docs/FAQ.md | 39 ++++++++++++++++++++++++ docs/Methods.md | 67 ++++++++++++++++++++++++++++++++++++++++ docs/index.md | 37 +++++++++++++++++++++++ 5 files changed, 263 insertions(+) create mode 100644 docs/Async.md create mode 100644 docs/Dispatch.md create mode 100644 docs/FAQ.md create mode 100644 docs/Methods.md create mode 100644 docs/index.md diff --git a/docs/Async.md b/docs/Async.md new file mode 100644 index 0000000..5954d07 --- /dev/null +++ b/docs/Async.md @@ -0,0 +1,41 @@ +Async dispatch is supported. + +```python +from jsonrpcserver import async_dispatch, async_method, Ok, Result + +@async_method +async def ping() -> Result: + return Ok("pong") + +await async_dispatch('{"jsonrpc": "2.0", "method": "ping", "id": 1}') +``` + +Some reasons to use this: + +- Use it with an asynchronous protocol like sockets or message queues. +- `await` long-running functions from your method. +- Batch requests are dispatched concurrently. + +## Notifications + +Notifications are requests without an `id`. We should not respond to +notifications, so jsonrpcserver gives an empty string to signify there is *no +response*. + +```python +>>> await async_dispatch('{"jsonrpc": "2.0", "method": "ping"}') +'' +``` + +If the response is an empty string, don't send it. + +```python +if response := dispatch(request): + send(response) +``` + +```{note} +A synchronous protocol like HTTP requires a response no matter what, so we can +send back the empty string. However with async protocols, we have the choice of +responding or not. +``` diff --git a/docs/Dispatch.md b/docs/Dispatch.md new file mode 100644 index 0000000..7b25a77 --- /dev/null +++ b/docs/Dispatch.md @@ -0,0 +1,79 @@ +The `dispatch` function takes a JSON-RPC request, attempts to call a method and gives a +JSON-RPC response. + +```python +>>> dispatch('{"jsonrpc": "2.0", "method": "ping", "id": 1}') +'{"jsonrpc": "2.0", "result": "pong", "id": 1}' +``` + +[See how dispatch is used in different frameworks.](examples) + +## Optional parameters + +The `dispatch` function has some optional parameters that allow you to +customise how it works. + +### methods + +This lets you specify the methods to dispatch to. It's an alternative to using +the `@method` decorator. The value should be a dict mapping function names to +functions. + +```python +def ping(): + return Ok("pong") + +dispatch(request, methods={"ping": ping}) +``` + +Default is `global_methods`, which is an internal dict populated by the +`@method` decorator. + +### context + +If specified, this will be the first argument to all methods. + +```python +@method +def greet(context, name): + return Ok(f"Hello {context}") + +>>> dispatch('{"jsonrpc": "2.0", "method": "greet", "params": ["Beau"], "id": 1}', context="Beau") +'{"jsonrpc": "2.0", "result": "Hello Beau", "id": 1}' +``` + +### deserializer + +A function that parses the JSON request string. Default is `json.loads`. + +```python +dispatch(request, deserializer=ujson.loads) +``` + +### jsonrpc_validator + +A function that validates the request once the JSON string has been parsed. The +function should raise an exception (any exception) if the request doesn't match +the JSON-RPC spec (https://www.jsonrpc.org/specification). Default is +`default_jsonrpc_validator` which uses Jsonschema to validate requests against +a schema. + +To disable JSON-RPC validation, pass `jsonrpc_validator=lambda _: None`, which +will improve performance because this validation takes around half the dispatch +time. + +### args_validator + +A function that validates a request's parameters against the signature of the +Python function that will be called for it. Note this should not validate the +_values_ of the parameters, it should simply ensure the parameters match the +Python function's signature. For reference, see the `validate_args` function in +`dispatcher.py`, which is the default `args_validator`. + +### serializer + +A function that serializes the response string. Default is `json.dumps`. + +```python +dispatch(request, serializer=ujson.dumps) +``` diff --git a/docs/FAQ.md b/docs/FAQ.md new file mode 100644 index 0000000..98bf4e7 --- /dev/null +++ b/docs/FAQ.md @@ -0,0 +1,39 @@ +## How to disable schema validation? + +Validating requests is costly - roughly 40% of dispatching time is spent on schema validation. +If you know the incoming requests are valid, you can disable the validation for better +performance. + +```python +dispatch(request, validator=lambda _: None) +``` + +## Which HTTP status code to respond with? + +I suggest: + +```python +200 if response else 204 +``` + +If the request was a notification, `dispatch` will give you an empty string. So +since there's no http body, use status code 204 - no content. + +## How to rename a method + +Use `@method(name="new_name")`. + +Or use the dispatch function's [methods +parameter](https://www.jsonrpcserver.com/en/latest/dispatch.html#methods). + +## How to get the response in other forms? + +Instead of `dispatch`, use: + +- `dispatch_to_serializable` to get the response as a dict. +- `dispatch_to_response` to get the response as a namedtuple (either a + `SuccessResponse` or `ErrorResponse`, these are defined in + [response.py](https://github.com/explodinglabs/jsonrpcserver/blob/main/jsonrpcserver/response.py)). + +For these functions, if the request was a batch, you'll get a list of +responses. If the request was a notification, you'll get `None`. diff --git a/docs/Methods.md b/docs/Methods.md new file mode 100644 index 0000000..71e29e1 --- /dev/null +++ b/docs/Methods.md @@ -0,0 +1,67 @@ +Methods are functions that can be called by a JSON-RPC request. To write one, +decorate a function with `@method`: + +```python +from jsonrpcserver import method, Error, Ok, Result + +@method +def ping() -> Result: + return Ok("pong") +``` + +If you don't need to respond with any value simply `return Ok()`. + +## Responses + +Methods return either `Ok` or `Error`. These are the [JSON-RPC response +objects](https://www.jsonrpc.org/specification#response_object) (excluding the +`jsonrpc` and `id` parts). `Error` takes a code, message, and optionally +'data'. + +```python +@method +def test() -> Result: + return Error(1, "There was a problem") +``` + +```{note} +Alternatively, raise a `JsonRpcError`, which takes the same arguments as `Error`. +``` + +## Parameters + +Methods can accept arguments. + +```python +@method +def hello(name: str) -> Result: + return Ok("Hello " + name) +``` + +Testing it: + +```sh +$ curl -X POST http://localhost:5000 -d '{"jsonrpc": "2.0", "method": "hello", "params": ["Beau"], "id": 1}' +{"jsonrpc": "2.0", "result": "Hello Beau", "id": 1} +``` + +## Invalid params + +A common error response is *invalid params*. +The JSON-RPC error code for this is **-32602**. A shortcut, *InvalidParams*, is +included so you don't need to remember that. + +```python +from jsonrpcserver import dispatch, method, InvalidParams, Ok, Result + +@method +def within_range(num: int) -> Result: + if num not in range(1, 5): + return InvalidParams("Value must be 1-5") + return Ok() +``` + +This is the same as saying +```python +return Error(-32602, "Invalid params", "Value must be 1-5") +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..7870009 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,37 @@ +## Quickstart + +Install jsonrpcserver: +```python +pip install jsonrpcserver +``` + +Create a `server.py`: + +```python +from jsonrpcserver import method, serve, Ok + +@method +def ping(): + return Ok("pong") + +if __name__ == "__main__": + serve() +``` + +Start the server: + +```sh +$ pip install jsonrpcserver +$ python server.py + * Listening on port 5000 +``` + +Test the server: + +```sh +$ curl -X POST http://localhost:5000 -d '{"jsonrpc": "2.0", "method": "ping", "id": 1}' +{"jsonrpc": "2.0", "result": "pong", "id": 1} +``` + +`serve` is good for serving methods in development, but for production use +`dispatch` instead. From 4c3b742d528156c4b69da332831eaf30ebeb41b2 Mon Sep 17 00:00:00 2001 From: Beau Barker Date: Fri, 16 Aug 2024 11:57:48 +1000 Subject: [PATCH 2/2] Replace readthedocs with mkdocs --- README.md | 4 +-- docs/{Async.md => async.md} | 0 docs/{Dispatch.md => dispatch.md} | 13 +++++--- docs/{FAQ.md => faq.md} | 0 docs/index.md | 14 ++++----- docs/{Methods.md => methods.md} | 11 ++++--- jsonrpcserver/server.py | 18 +++-------- logo.png | Bin 0 -> 17627 bytes mkdocs.yml | 49 ++++++++++++++++++++++++++++++ 9 files changed, 79 insertions(+), 30 deletions(-) rename docs/{Async.md => async.md} (100%) rename docs/{Dispatch.md => dispatch.md} (85%) rename docs/{FAQ.md => faq.md} (100%) rename docs/{Methods.md => methods.md} (90%) create mode 100644 logo.png create mode 100644 mkdocs.yml diff --git a/README.md b/README.md index caa0aa5..42618e8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ jsonrpcserver ![PyPI](https://img.shields.io/pypi/v/jsonrpcserver.svg) @@ -16,7 +16,7 @@ pip install jsonrpcserver ``` ```python -from jsonrpcserver import method, serve, Ok, Result +from jsonrpcserver import method, Result, Ok @method def ping() -> Result: diff --git a/docs/Async.md b/docs/async.md similarity index 100% rename from docs/Async.md rename to docs/async.md diff --git a/docs/Dispatch.md b/docs/dispatch.md similarity index 85% rename from docs/Dispatch.md rename to docs/dispatch.md index 7b25a77..719ea9c 100644 --- a/docs/Dispatch.md +++ b/docs/dispatch.md @@ -1,17 +1,22 @@ -The `dispatch` function takes a JSON-RPC request, attempts to call a method and gives a -JSON-RPC response. +# Dispatch + +The `dispatch` function processes a JSON-RPC request, attempting to call the method(s) +and gives a JSON-RPC response. ```python >>> dispatch('{"jsonrpc": "2.0", "method": "ping", "id": 1}') '{"jsonrpc": "2.0", "result": "pong", "id": 1}' ``` +It's a pure function; it will always give you a JSON-RPC response. No exceptions will be +raised. + [See how dispatch is used in different frameworks.](examples) ## Optional parameters -The `dispatch` function has some optional parameters that allow you to -customise how it works. +The `dispatch` function takes a request as its argument, and also has some optional +parameters that allow you to customise how it works. ### methods diff --git a/docs/FAQ.md b/docs/faq.md similarity index 100% rename from docs/FAQ.md rename to docs/faq.md diff --git a/docs/index.md b/docs/index.md index 7870009..3612046 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,7 @@ +# Jsonrpcserver + +Jsonrpcserver processes JSON-RPC requests. + ## Quickstart Install jsonrpcserver: @@ -19,19 +23,15 @@ if __name__ == "__main__": ``` Start the server: - ```sh -$ pip install jsonrpcserver $ python server.py - * Listening on port 5000 ``` -Test the server: - +Send a request: ```sh $ curl -X POST http://localhost:5000 -d '{"jsonrpc": "2.0", "method": "ping", "id": 1}' {"jsonrpc": "2.0", "result": "pong", "id": 1} ``` -`serve` is good for serving methods in development, but for production use -`dispatch` instead. +`serve` starts a basic development server. Do not use it in a production deployment. Use +a production WSGI server instead, with jsonrpcserver's [dispatch](dispatch) function. diff --git a/docs/Methods.md b/docs/methods.md similarity index 90% rename from docs/Methods.md rename to docs/methods.md index 71e29e1..28457f8 100644 --- a/docs/Methods.md +++ b/docs/methods.md @@ -1,5 +1,10 @@ -Methods are functions that can be called by a JSON-RPC request. To write one, -decorate a function with `@method`: +# Methods + +Methods are functions that can be called by a JSON-RPC request. + +## Writing methods + +To write a method, decorate a function with `@method`: ```python from jsonrpcserver import method, Error, Ok, Result @@ -24,9 +29,7 @@ def test() -> Result: return Error(1, "There was a problem") ``` -```{note} Alternatively, raise a `JsonRpcError`, which takes the same arguments as `Error`. -``` ## Parameters diff --git a/jsonrpcserver/server.py b/jsonrpcserver/server.py index e783082..d0c54fb 100644 --- a/jsonrpcserver/server.py +++ b/jsonrpcserver/server.py @@ -2,35 +2,27 @@ http.server module. """ -import logging from http.server import BaseHTTPRequestHandler, HTTPServer from .main import dispatch class RequestHandler(BaseHTTPRequestHandler): - """Handle HTTP requests""" - def do_POST(self) -> None: # pylint: disable=invalid-name - """Handle POST request""" - response = dispatch( - self.rfile.read(int(str(self.headers["Content-Length"]))).decode() - ) + request = self.rfile.read(int(str(self.headers["Content-Length"]))).decode() + response = dispatch(request) if response is not None: self.send_response(200) self.send_header("Content-type", "application/json") self.end_headers() - self.wfile.write(str(response).encode()) + self.wfile.write(response.encode()) def serve(name: str = "", port: int = 5000) -> None: - """A simple function to serve HTTP requests""" - logging.info(" * Listening on port %s", port) + httpd = HTTPServer((name, port), RequestHandler) try: - httpd = HTTPServer((name, port), RequestHandler) httpd.serve_forever() except KeyboardInterrupt: pass - except Exception: + finally: httpd.shutdown() - raise diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d7024dca11a882b994b4ca9aee8549294e39dbe4 GIT binary patch literal 17627 zcmagFWmH_j(kMFU3_eI8xNC3;?jC|e2=4CgGPt`0_uv-X-Gh6u0TSHZd7Mw)z5niB zt97@Q^s1_^j!;sNMnfh*1^@tPvN94X007MOd)@$q_#Pj93$T8#2(}a#SCSPMCv$MJ zH?y=c1pw$0d=ms^`bCHa^;HKMK8T`3x{N4gGloaYH9#AM8VGhM`vdhmjtZ0G#sq~4~Mll_d62zGeEwcS@{YZdTEAf(KZ`f zlJ19gJ*I>0f)Ceb)Pjm)hy3E`{#)_SKze;Cv`980XNUJc2yD=4N@% zu*1G9tAtNQ45PM@T~IEWrfu@z5#f56jmVVjMu-m><;VQ{Y$hpAokI~zS1$D+He?!d zg=^lZV>;n|iHGR?XN-NxG8A)|A~il3gs+~g6dFQTMLn#?*G*N*N^{47JFL$znZ;A|0i2CpKz0V9$!g#5n`u`KSRS`_oI&dU!`vR_=@#@c(?zZ*4~h;1bLGTl~rC|>HqJt zi{M<2eTgkxf^cruVE3P4O%jpzaft@mNvu;>40Jusc=JvtZz*g-vZfo=cQ zP?jdI1oZn~Ko*UoZ6D~^40+?f_E?l!)L*WerN>$#({-qOK%jNtV>Ooi-#yHN?ErLP zE&(TfbB6qWtc=9`0n4@kK-Vwi1hh=dj2rr3gd^pSJ z7l0{W;2PGrVJ0K?g2pG5U!AC6-vZAtJD&X`mnivqbx-THB%Nt$Gye-h^F;H9s;7|# zeoL(~?H3HLtUy4;hiVRmkduSAfW$aTu~ zC?3qZlPOdO`ADnB4t`$V;J@K^K#H>eMbdK7ky7LrdwR`Y%N4SGd`#H&{oPGKo1Ou8 z@A`kkK~1%SDN?nPwOQMMw=JzM>|{Y4F?>)$!apu$Ai+y1PG4Jt;m6#Z)VC=B*H@@ifU*COxT5(>@J|IX|xN{z)>x)ps-HRh+*4l&xjN`ew)z1M3E> z9S&OxH*1mIh#wc?Oc6F7DVrmJgX~#{-13l z?Q7`?qFq9pZGeavCS7jOl1-Ol#xhia`k&B+fX=es?0*g!CPShVA1gIST;Shc=QCUm zavANt9;UVSM!Y=a-F%B)j=oGgD*#7FreAd5(VlPKr8KpS{ZFkOx0KdL_rvl6y!4X` z_hT~`6;0jMNU5PL*QVz1OtRBb#*h&o-diQFm1W)@`fvockah_4R|+a#eyQK(;C_x% zX$M_Q*LDw2lQeN+_xOpXhLgm#okk(S8#?V?MVX$wZ+EJX8RetlUwzemOSz5i#@J%| zn|H3%at&5+?UIIpXZHGdKY+X^+s{>_&A+D_y!ohnMRI{I;xi&okgx5hn?3)QMitPe zW$#g^vBc?odv_Cyadt%OZ9j{glh#ncOIsEH_LhwNwy02qzgNLUwWY1j;@*gE6Ctlh z2Qbkx^xtOaJI z-Ap5g(*I}(+(*N#ElpbuaH~~p1vHVYi|C-AyOIQdwhwtr)!3d@Rm1u>9>k6b=Osvx z1g{haw4*lAxcrkruoiQCY)p*J)gQF0KJQ;DUaFRR>FzD0x^X=vtr`=Pg6Fiu3Zo0m z8*em-(SJ$Q-ovd4nG0^Tze$!AF!%)V>Zc9=G8R($YVuV{y8?#wq7x~(J_6NVbu@mX zj$5f2CykC`}sHDge0*^@+29S_dX4~y_~(?;|*^L~T>_0Hl)oJ z=ueOfBkbr`I3D!*qp(^_?){<2e&$3Of@g(ObQK+tQFGAaOCEJfo}61z&ph@CgrCQO z_746RhsbPJ9s~@%fK>m1(b~4fDiOcsAI1%Gu|r8bg$2Rf+>2s-6ZzgY%t{*_Ep93F z#;ibZ8&Ppk3k=iHD=gc-_b1C6SI~V~)1=7_Z_xf3z(YJ!$~!|%3(t9BkBk1{P7rc_ zylE=vA!Y=ukU%-Lk@4T=Ye6K2vlb;Ws0;Yc$aX~z%EKfH91fcP$prGd61>OoTY`6& z<;)l7IZ@O@db$WQu+@hDUIiG2(RopaM^=J?dV)w;`uN=_Ip2U18_x;y;GfY_yTorm z0`yN|JdXI0Bd3Dr(b)_yU#p34`Dy!vEC7{*CnCqU7QIIO?(~2hpuTQQ4~87L<3)b3 zq*;L|w9?XDVz@|JD3IJk&jT(YJ!^0A_5o$=#&kfD6lmh z7=~LkFEh0{%e{t>os}a4LKjp<&sJq22O`ig1X&p$krV>GCF?Nc2qc{fW3`tb+mTyk zGVc>|;;CG33*aFN7^Fzm7X`#ffLEGHxBgXNc06U{NaW6TYfT-edQ*M)(7MvmTHD`5;rcn>0tdB4oSk}{NF#&TPsQLs@a$7q}xH`TG9p8Zc4NIe7CU^FzyRAOMUYT;emz@Q6mT6 zEOGD-c%u=RQ`fwoS@KCC^rpqw;A9O`M*%&24#}t)655vs0o;!R+w4zzt$RhPf z{|4*t&((2-N6u+YvVRCy@EZnm@MX-82qs`s6B>F0vA%q8Aez&^yK-Dvscm8jwH`pq z!`MXHbdSQ!y5SAhZ-x`q( z*LS+V1XL0UXEra1psx#f)%0nlW9YKUndm25e6E}n#sAJHUU9Y_x&x}|O(O**Zm8h? z^nHFHD$4e4vm?=Ra)$0N0r53-BYQ|Iw*22zLF3#}sJ0!aSXFfmazOlP{7KoJEgnBm zOSFrt@p2iZ!&hPGHCxY1GkMDe3%pQH*vOrKfL+zB->C~;W`~z=o&wOKnmASY1=@BD z%M(XPCEVmD>2Q4(6+O-zdiInH^?$Vmjo&TuBSK31hm7u}(Ip*b%vRDHZch;8{76*U z#O|MRldq)LoqCAp1Rs5d(WmE;w3jAl6!Lba;Bz9$YA+S*cT;|zl0$V zw-*jMT)4M6f{7X2M)l;rYu{U>a-;u)iOvi1;F?8-N!AK?Sx=G%ZRS~$4)j>j;U2Ac zdz(q*%Y>)vAO})mj92SiMdk!4dKp8K**&4i2J#@2X7(}I!}E6_|0M0uX9{>5!ncx} zjPwBlPOrOU+D}z@9`6wLTv(?s6L$udK`J}0;>QaK=I+Tq$~J|IT0^SMZHlWmxP zuTk*NiEj;PPCIq$LL`%pB|RGz(wws<-^gI4a|@yS&NzJ<#3IlzYS8nYt2*IehZ_qB z%J-q4>j{Y`?x4gw%d${0^nEaou6xAZx31XprtF>(%&$|HC+pZS2=etf&8AHd2)A&8 z?xc%=AA(MyVqn!3CL-n-^4@f>13Gc^bQAHp{|m8L+=Tit$|5np4}7X>7=>-$N*f%O zQ8W2Y!4*`%lc$5!C-6Kew}6ZBBS{V>pMlNgHoTDD{p?S9sV#pRh%=80J3BKaoFy$< znDajPeK7>DgnG$Rtn6gDY-h49q38++U8s{=0Y~@b4ygN4ZLeqK*CRyKj3(Gw+ioM; z_n#S8R_G0=MR$44$(-`wd&BnT&ln8sMMrs$N7{^xI47p~6b}wYI?i>zx4TQOTdJ-U zMeP{8ZK{uS+zU7$o-&b06a1=a$7)mn2D*39f4Xs6$FJnhji@YJ3UqE&G5&~*67M*P z?-27bG~-PO{Q5-^25;RJTl_y+ z1ok#d_(Ai3?c1A1T93?hS2!(qi zbl-B5A+7KRG||$F1qWo5ZK8QNV!qy*Faguxh3J!*wHW6uK0lcx+h2cmnE$SEB1~%7 z+Yw1WN3DD;E-6O*O~}{ zYHq7_m0GqwEuUtCt^Z_{O7A>Un2)xW|?NLMEbqcv+SAKb> znqab*ft}tPek?K8k)fqE9ZmiKw+HCQ8@?8|pnZZ=>tV9D9SuWk{VaCYIJC9EhNLh` z=NXr2J6?+Q@C(RGhE$J$5MhSnEAKlj(gW=18oz|s9U~4$+*X)(x^N(~be4|hPI2Xt zUg~*cHEB=6+peKG@#vcX}xs$$kad*hXczpx#pQk=^|phG4gG#^%q! z*g^zHI#z?A)=}WKrX*WtUfA&v@nZg<%X-5ZP5xOC$mTarzs-l=3nQI(-yAG0>jRwk{llJz#@prBw{HwM&!$$g`pS-_;X)SWzzk9aaoz|+zP_xsZj~vU5%4$?YZ#2 z8M2dd6l(Ul^Iz*uxa`BZD_>Is#dq;b`nY!9s3r{-`wnL2tvF)D!2?&KQa2Kl@*4e# z*$V<~I^P4b%unH%6!5qktY9$a+g%Xo=wPiE>tWgkVXHN`M@6|7UprTbBKf%c`0pTr zeV=dMxyeQE3x~SZ-{TdygSZX&L#PDb?6D5i^FEC+MCp_pX5FHEba`$lGW>lZ=%4y8 zDn=8ZnoU7nkGiOpk#kD5UbrpS4Y=ujS>w!zymi_~{GFplke%14!dNL3)biWgH!dl^ zDhRvu8;Xm=uvcqDI+K$R?ca4b@vAuVXZ%`u-eO0_RN4M5g5~a&Q_`|>sdY-0h+WF3 zgjK^)Q|-IreDHk+fdm6Oq^1l8P5MXI+5X0d$+9%g*h0xHBT8HzVXKA58VBdw zgJ;Jo+}q1p`s!jLtc%gHcZ~92ZT>`BEIpI>LiZWg1Gy%~Cg2H^SbH+bxi$Xox|2@$ z^jE1-V*$yJTcJ*9@dRmpyT2}SR~J;)LXz0diKOd8O34P!Q34U^h)X%qND;t017PP?R904 z8uBzh+^9wd@Z!aW?RD1-^^~<{Pe}@km7)ZJ1N6Iq9ZSHd4FClEeHHh!U9*jgTAQmO zfw;q_-+8-r{L@0`SAN}KCD1~yPsDVjaBtc-*e#HZ89}8WAEo&$f&=UF&~v+eZ?C&w zWQ45qmEp#-#m~|YB9B`O-)StZapYx|CrSHqa0_tq>iY2d9NRK$NHf?}8x(BuXVB6M z6|F8FT-=V3S7Ua-K3kdJ7Rbl-x@)v8CY6z3t<3%Hd{9)dRsIC{@gy6T&bl}E0!$9dLAD(5|-E%tu&slR!De$*_v zCOCafGy)K8a$jO5nsz(I@+w~cOc$|@=Vc^RuO1z8=P%+@u@wD@wkI(sr%3DyKo`!% zi))tS%=gUVlZ|COuY|8qIja5xLF|@QJ&D z#kGJY^b-RXJD~K_?XOF5O3Y*hU$EF35{l)L4!pt{2MBh|yaO_wWm?`*mEk~2^vS$; zrMO<`h1V0loN&=Da|Y$QQWwL|JAHou>1~(kYk=Lv1dMGGTAN@-ij#t1n0zQaOxLiG z*w%Jh`rp$YS9h{sHqD27W0PG-#2S96nUkf$&hsFTbFa?R&!nVQU0Pt*QV^Hw=@65U zr9erREZ~T6{GA=Yl6+r(S4N)=5%zz!#ELjJ?|C&tUj7;@vp$FwxyBX0(M=U@Z@h^* zgn|9DtR|P0LkmX^?eTh_)~E7Z?UeKYj>}umOGqQUuE_6N}Saf0hG~_Ac66IUNDk_w37GUrF^$;aI z0syHBCkf7{=_AKWVVWDnzHxy-z!D9%rSuIu=T8}Vrtb?Q|L-eb1@EF^Kbc0QrM_Jf)Y!(5hPRv{FYlo^qvU2(}=SK4I&6Js4g zfGVg4Le}Tn2uexrZ9ia1Uc1Aw5p2&hn1wHLJ&B&ZGo$>~uz^jj+$h)mBqpAa;C_Xh zjP@2+34`p{gw^;Xw(ntz{HdkaCLkvlC|AQA+WF_QH8=k;!+cN^tuo~9fn~<67iEOR zsgg~kWYoJOd2eg86S#na*MGNQ^sYGduonQ?hvFnpmz5nF=%a6)Zh=btH>S!vIQ=uY z#ie?egbc5~C8!pakasZ++U7{ax5kxHmYG$>N{WMHnKWq@H%Tu!5hthQ`P!*6ymQo8 z+HOn}iQ66hHJg5Ni0$Sm#6h0vn2%vCVj{Amewh61*zwm4|JfCVnsNj*r}O-|ji|9+ zNOE!^jqF{R2B{N?t@Gy&{yBoJbzu(dZ25loea`DvByCJTgQ~@Wc$Jjow}Mwh_&bJ9 zmLoC{Hv8Xhroi7w158a6P4rmNc_|eGJB75+mKJ)i&Vh-`6e?Pi4Y3L~pW_RjhaB5K zwyQ3_bP?S~N%d_z9lk2`uIwl&@iO0tq$a>v*HnXyEN9cP$iczpJ7)8fPq@93noeZSW(0Y= z`wjy7R}f=A+ZGI+7hGY|F$JB~RF%H`bDv*(L=K;Wg}=9`@c>wg#~OCUd7pSC$8>z- zDhCAonzMruzBBwA^NNS8f;siQ9EC%(WnH4=*i7$eD!ZP4T}7&lr~kU+?H zluI-$Mn!al-nc{kpn?f4Ai1V|uhGx*28frN-J~HUagzU#Wu z?3wIPrp`XaPdc!MGuD1*sx>q1%}5Dpu+xp_zoa_%6ZTnkFR<#pJAXd^L}85{35Xtd z=w1A}I?qX**(7=MG@TTFD?pOm@l4a97`~c;9Q7u)u-&Z`Vm`f_G#t-;Nu}mn&-yJ; z#E8kgpC5xvMWdvf6+OV&!5@C(h5nHU0pY80weOa%iJq@$6pr~A@3$re;-uXm|4qMS zp*x1avuC+8wIt|hF3ziRwid)_5@!XxM?(3M&Eo#k2MFH%=9)NH{3mFKz*I6O}?`A(C~0N5}d<` z{!>>Z?+VMCfu-Hd=`8iSR(B-OJgDdBmOjBZj^{S|p|CfDlwlcGA!pu1XEmWUzZlfo z@jURVa&eDZ)cuB4d2>U|NhHbM zV)S+LVZz`)F|&8`&q@h5dl>pq7o2JM^RL1L?jm`;Bv*A4w_}c3NSCB66XuEkA;P_x z59MH)2k@bz9im7oJu$&ybhg0FQUch&>{tMT!P$gcc#QWt zA}Fuc#H@ip=)1F1A~GC9_Zwy}%eT=nA21h0P2{>3IfK~N}`3oFqHF?SBytPQMu%*Q9Sik=p%Ui7O<0Pyd z7&a}+le7(tO!?~&Ko?@8xNl*t-E(!A7OKKNS{anm!Nak5OxcCu_3{I2Pj&56)Iwc! zRNIoR)!#U*?rr@pZd?d*huuo!waF)T)TKr8=W+CjI?gUUDCVoI-;9 z%Umh=6ZmW)o=(XZzW~34u=Ea7fC0qSXtUb`__k5NhYljcw+{0_`Z|{ z8^SWCEON)_80n1`P#*YD>^Cr*>@@xRsJ=?%d8!~gGJc5%drvIMJvkv{XENV?*ertp}3H9o+4fF)4G90y!3VufODdwW`IV zFzZGzjaq0dbHPieNn?^h(F`N>SbwrUKW}pTBROLu41b_BH0F_`(9sBkPIf92qrq!q zkXC-{Ke+wldL#)6Ut6P{Y`+%7o{oXE%`n_aVW@SSb~ukvCHvmN_1U>6#dLaXhbY6* zvG%F8GfEmWxc49zqg@q}##88#dbcGrptx;++BQFOBw~u>_({>_3+zc-90Me=*V6Ju zOQp;$miY=|%tMq+;9Vy9{~-k5)tV+{*&?gg^Jich_W6kZ)OUL}x3bHD=r(U+yq`|; zKc(^4+j@8(d4lD}w9{<2!7(EfnX@+#e+5)MtBIv+GC^H0cnAVgil5e&b(y3LQ*~98%U&t;6U1# zh*qVzta-l}`*iU3tsRA0-*25t%u3$5m&=qfSR~-U`ygo&$1^%&*z!v(I($e~3zKTV zyYzBA!%E&(AOHaOk;4VpF}mW?eF{>-kB_h_jTV&<%MjFiU0&dLw`=N>ZSYhVaIVKF zThIkh7s&s{lv~8spk)lA>nu`6a)Gqpd%ke^_iUh44{y-shM8N>nRGc#5k;Lt*u~Rx zY1j%r6Vh=tSx$6&*BCX>ItqOd5uCxld2I}t7`i@1(yo4`RQ2_~gKVB5B1pEBX9&tz zvP(IHD#l*F{?c(fB{csrb|h|H*zZj0g@uau@Es{f{X9yY?#bxG*g2h!Mx5yBa)Qj! zEYj_o&Ud0)Dd|8ktV7l-vwC<_r2LUTrvSfuulkz8-QAxoXGE)0&!_{oIWISJEsQJM zO-s6?eCp|<-j5eH$eRXa-#;WF!@koz1HJQ~7X4%@0TA_QTBFJB0ooCLC#GJ5teJKG zwIMq*A)YOHCGEt)jTB6MB@uII;PB@+A-K~TT=L5!1$iMy!As?PXbc%qn35&081M`D z(Hdx>&IH~S?H2%}!sLUXXplvC2pP)?YiY0@oQfn^HJ}gV>v8WxpCf~K>J1)s1>eE0_RXA-u`(9?sNw6b z;nEQ?I{5q{I0AFhzt?b8r5Wq+*Ai1vk%b(yT)^viymzHNu|RfwuI8`iXPzk` z7|A4NkynH47*!CYij35?VlmI*9;t2kh`}x_ko#0*P+jp*u3kX^bH#UsF9E^rIM%LH z7Bf%o)``PRjc$tY(vN6v&mTEGEGCaF2?F)h{&fmh5swJbd0zy~G2ZWOB z0>Y6#u9-v6sYTnb0jG?NB4M@tn}$WHW3M>Vgn4FMvHyFJK?P zb0Ja&;TPmCyj|Ix#G?!pL-PjVmU9h+@@ju{btJDP*0)qc-2|>zHJ2=(y3tONz*D~knjrJ9Mb95WM5PS6tvkPmXPgIz$zJ|^=@DitmOSb^pL)bMtrs?( z1gi<(Ghfw4P4QxTPHmPx@Q&A3@890Pe9g`bT;O`wo`-WbXyWB-C=m`yL&I};Pvq%6 znrTzr3Lk3{7)*cj+kx9&Ut^Db2D&eEI$?Od`0JIC&zgwy@=BjCwRhP}Nqup=eD7eg zG-4C?)Kk&2Q#+daZ<~dN^dl+1t6g2}ukS6H*-Lb3nEn*Z z7RQa)^DBEE$$=IQSe&d&>&|V~^Pl*eu3Z{BZ=ELo1xo>Gz@jA~91efWS4RAIy#{;8 z(s3$#uAn=qch`&$VB$-;W_5Bk^>wOm%bmt)GJBNcZvF1iyjMVRDQJ*XXpP5E?cc(A zI*FWl{t=5k`4dF8{B~%+_1cFOL=aiZJX1Ew3u(pZG+o1ERsa}LD4$=d{UzZ5037Bu z&Wz3~?0Y%Rq^Dy{$7U?L;95)SZcO4lIspg_sc#Um(BzMGS`Gt$qY_+?0m)t5zyxCN zUX50W0f=e>My?&!BvuHMm{xBjxeY!*#FAbNg}Y~FNu-&D0pVVas@O=<@puXAu+W== zL#?17{-QgHD#=d2qn99a`a7YHv^E^*|hqz4(f~6gXE+f&%RK+%;5Gi?d35j@_Oc-WTF! zvxfriH#!JSHH2s|vTlT(d?S^fdnV{g&oa_h8d3Q4*^oATpOjyQ8RlQ_2`PYMV9iGC z24)kTZ*&M)O-$uq1S8U>z7xq(&*JNI@gHydIf`n|z*J#@o* z7hndPdT^V>5Jo(|+xOek{J=#57;ibgd4Msq3CUilv1?84XC;N)jS){PfUq{QM z$NmLympmT7uIf*jg1$2)rnRI%_hgb&nvQ}1ZM!xxd9(g_iHIiQHH@Bk_S5}1;5wFg z+VFV=r1p(M=^M;hY<6kZ3$NH_HAmCeqC%#Kjv#vAZk-}RlZ`lT|H`RX*GA0P|#byzOkY@RN^sMc?91P_I;vY)m@&YN|N=2g+l)Q z0Tj&AQjxq$SXV;s!o!;t=n~bYMzEEMSa{!*Suk_DO!k3t6g4&dT?65i$lg(PV&^xh zJ%y3|HKY>-QWSx&gCJ25zYG%WVOR9+E824{^1=}}Sxq-vi1XineF?B<{7RY;hh8k{ zpA%p|Cfxo7WIKYsC@V&C&gWz!cqa-D%L&C;Phvxr9|nDJ&{&2A5DIx|_Y9 z6^dMw-R$dsmQdP{ko`p`0DWy0OI$FVpo0-vIQvAvQhx*~VKtci`txiLZ9i_!&leJQ zYRA}PGi~0vIat@6$7@Qo;$e0mj` zBYKLBnu5_;y7fM406B>(7JaqXaIcUbE8n_T(9a(1*r;z?T1Wc0C>%fFL~b5qpcUXR-OYFSkX(R@TbSo2vy}eLcYL zJga}XOrL)`WD}+=4J#~Lb9fUt#R`VX$5rBr8<04)E8k6QFQ1_-?m*pzYsaNtJl@$L zYhp;pa#JLy^9lU;9G5&%MeqYmjbF_|9|`>6=Z{1|V*lkquU)wnCEI)vR?Ys;@7Io- zE9UJWm?y1f(@*ywyfYN+Bb?L})NFH+_W$JBv5MI*Z=wRSNjibI$H%+){8PqL*>Mpc zpo29I4!s4qnNz<8h5MhjHNK`};$Gtv7o)1RV)r*zsC6e|9%y~Hmdhu8zIR^HT=rg6 z`)O2^Jo<|)&t;M}rWPikVGuKqw_3faFLRQemrnCD}*n6Fd?rnAn}B!-w`xubfZMkK<#? zk84K+%3MLP@dFZSiFwPl_Sr>NI_wE(2^z~LDBCKsU2s5UjwkEEJNLC$u!sMZ+hO@? z3qF^-RGKCGX=jUOufX=rR!^s*+*_9ow_Eq4Sv_~g3jZBPaQA9_Q7yJ4kW){g*a;tF zSAwG7MVyy{tC8M&F@7}$x~O}2`M7(xeAT;Vwv<#ACkWTJ8Do;59s$&v;Z1Pv`zm4% z@D2@SrpR*ep5A={ZXHhh&~mBQYzBxcc2cwOFr&EXxp1uUyK}F4h&8${drlQX{nvZ< zrRalbBCyR@IC0ogooMHOs6-A^-*rtkc*~8ok{N6sor!$!)OR~c27spYqt7HmXz zSGwT5rFA}J7k?$>({T=|PTE$%3JDQg<~xx+Cmb_*IPtr6K7Keaq$%r;UpPX`bl4x| zmiV?&`kM+CX(oA2f)#IEQha9*I;l(Vz2RU>9EhS}>32Fk!IEdPbvN;omKvoyZ0})8 zQWSv=4BFp*TuiEVoqmB3R0gZjPX6K>eaqIKq@<1?xnonciL(g->9@N(sQs68O zAZoMZbL(QS9~JHN>ZdstI70;SS4dde6%ojU|B7}3@ZhuB-=hKpp}A$_ubytFh*sk4 z2%fV`5TrXOw0V=ORVVqS0UpTQA^khZu}6!qZ~O@q{L)+WiQ zno7iOAC{(T9`VK=$`5L-UT96|T@tvz3H`!<&Vhnmi%42g@wR{f$myle!2M)GPRK04^XZ!>*fc`feh$hdnQJUOj1!&@ts3JF)bF zm;dL_d|cR`^1~DRF{!U$Fxo%1E<_JCy(iAwZmtU1D1J&k(k8(NB7rGinlqf?7cmi~ z_XBA8bxjWX0--TLmwc${Hr|WS05rl_TBSCs5~)RQgl9bu_2_gdDWF>Qns3(Xx_dCvf(oEvfPy^SV?M&6aZ8pm_j}SnER75Bg8nGANXid*v%P{!> z?>7*AwVTQ&M4yLMl-k4eA%gZ(f!z&^jmi$DAKbd1VBBIdD2}?Y#Hi>GLzgz{xDA%Z z?R)f7+medCetl$hyNTvAcn(X_0t#Y`Vme=sGv?7hGo z`&r{MRQqa$^tBWfy44K<0DibAK$tHiMcl-GEypF zGX7xgl&4hGIc{^E3p{xmGpJt%s^zZ5`GrqtY(^h89;trD^_Qi0>%2g}&Bo70!2+8U zs(;S?&fH#%z8mptoPIfR$TS@X2Sp_(?5Qly_`rrYQz~5I?bAHpdKRBrH)V@Z>i3y}SqXV41SD(}yh?aD0es@*g? z*xR<-`}Rld`1w-9L;*hzJx!6bqn^&86zfUPADGj+2{|p3w^jREHb5d00+e90;)c=1 z?NKjNWwt?Nn5PuVJr;bw%GZURr1(akvaQ=kDG>#+fF=Sm2w>QE_f0| zkYS-x=%_}OZ>HvNx0$~NbB*cyph!0BMnzM$_iwvM-7RTV8AZv_2RnRBFaSe>is{l{kObzs)!-ec8ZIbuMgTi@X-!wu zbXo;raSH2xf=TsZ-}%HnqPCe&Py@a3%x>b<)}a!rwV*!MFm`qn>%9^fa*3?Q{D{eQ z*HQN57hFp%Fqn?GKf*{K)S6fg@23r^Oj7*Hta`b_(&jZXzVge$WGm`<=+eZ21s_X3b+IP$Sishj=IJ_^G(DV!KGjlk_WLF!il z1cIyD=l5V{CTWXt=3svVfkLgpc6DsvxMQ}#*(P?=?MY8@YY#6m0A#@m>LnFvDMHP! zY6m~~)svf#dVR6~o8=#RnV})Zmr_$DKB3D|+#_COIdW`X{XTx=QuB6l>@1HE7%6=5V=Cr)yr83i1r^qdKyjYc7y)|W?f6;G+j-h@|Fo7L72S^xq`H%VA}h z=nP>@%Tpfm?4D>1 z$@oNt$EY+zVHOqpe6s{qqBKLQeEzGsD2~A3iq4qF<;Nlf(+rv@f*o1aa{wPP18e}L zN|#9kYhLp&z0(`))f%S#wppnyH~@RWC-&TyIq(m-fcHDnwnDRSHMzti#Uk&|I~4J| zO5Jj5X~Yg?Z^{FBW=;w?ufw9M&gLFf%*2Y1Uezq^kZ@GZv%WkMBUvhb+g5v|9J%RK zg|jX&qKkl_V#teiEr?`sQCA~BP$?+TOZ>_kcEF~|rovJp=gZPBn8r#d-m07R8FLRv zf|WH=tIoNUx5?QwP%EroJr|5s0woVven>eF9wCyhG#PI&ktk{}+HybeBh;1Cr{9Ii ztNU6KZ^gYd{b9s!PrcUVF#J6CNVMPZyO&GI%g8CzDZ1I}hWS-~q2(~9NOSf;3wD^` zK?VlGl%X7*70za?Xp)mPB7mUmphp00?H!8e8A*k^l_&{TdU%i*KvbX7YWwRMV^I=< zUHm~7mdX+zuQgmwB`y!FT+5U4xJ{PoBHS)F1E6ICVNPLFKXRDb?Bti{^<`vJ@|i23 z*kEBGssOPl+M^_1EhDaHh6B#iY zhA9c-_eWysY$Lc}1pS=iY%x}qrTb>PKE6MsQE{3GUy2$z#hNIMu#jBfd-oznL)UJg zyc1Gq{ixPoiY3m_XO^5S@_D1mBJ1t3tXR!Pg3S25QAC4TE^k9qu1jTEDUEM()`A85 zYbk>X){0CFD~qXxEZhRQ^rL4ES+|6|f^j&0CJMt5`kcfHYdqKV7`l?AQbI0^pg}J? z;z}KMX-#28Kx_t2?b%Z!;(6>baExc7d-=fdh)Q~%WH)CmTvsyi5Iw~O z!|%ufZ`}&enE>%^?=^CWioklfJB73I`91UgEE7z4UJln>U`R0r6T3{N+&Yj!PPddB zp`>I+fdRETg^cthwTv*?$;X9LpoMa|WGnL~L?EKsif;3Ofj|PNq@toanJPU4E%I=i z7(Kh{SAMLztsG)hbb_2?bDTgUB$Pv|Tu)bu~V0^3?kS@Af zxLc-?Z)_WTsr1r3p@2#m`j~8_UYMR7%?>dGL%-xAHRS{&(#)iXC9hiiNvk1jTeyv} zqJn28^Vv{08PT~6D&qpcNl+QgX5kQCu0*dNJ@zq1!kCHljV_Z*_lwwd2`|#MFrNCC zZkoyx6-`qOQgH#L3dw($^zLvmZj3}SvDiiPX#iY_hFiKlo)PW_rO%gNCX-YcCAPC@ zsPVthR8`^mNMiEHejhDnOh|9yDD1S6G$AG~pj;Y+#c9hjhEbq2xuUT1N-#izTF7x=FT1K1U@nZpzAIuZ&-tADnyXw;;;BwVeFW6wtH z)#sa!6B$Y8BcB^mdH)i?rU#IaX2%Ir{##=7KZ0wGSNbheQ0oeR_#xhFF3yWmLvTz{ zJz0#OogKFV8xB*D{IMES`1CtilnNkXYL<3*-GB|zLB5rV;KM?}gS&pr`h}(6FnbiS zYMlHFEwV=nYmbv;Z656t8yS$1U$it-Sp(J?S6b}ES!>GPglKdhnXGC@$wi7>&_w1F z77hE1*Rx94S{omxsWM%Xae*07!nn|eJ%14Un-JZF2(xnu{BG5W!1Xt{{WVQE)H@3e zin~zx^UnK5DvCS_>KmkoxwfJ*j~99xT!;oX$_iaJ16-H#k6GI+q)aDkUWVX-;!W)2 zY>VF^B#PQ-BdW7$nL8pxO~DW3zrHHlv88`(9ItsBmvr{>po*9-Ynv-Y)(f79|MCb@ z@{N^ZAePbGgB&_%ph<=X6NA*#V#1wJ`<@bhBwBj?lbh)Q(nDV3g-($dHx! zQqquoAsB=h^F!sd97aWwH^Q*fRVBHMt4AwzA=Xw(PD?v{o$mjbMU`aT4`n?GFg;8a zGc6bplrMsBka^e z*abxS+67_poI+^o>%&z(Ha~A~`5L8!7@ZkiE2Htg9y-&Jp|E+Gr0xH0Y$6#vCCdgD z@YxkAQ@F&crq)Q9K49=IP46(~x8$S4{!{0 zQKjcS;o>iG0GDb-^Y8603Zsp|B4E=qU(lJcv&kKk_1TMBY@$gaMFAZ2Lx*r@Xe0xO zctI)h?`#N_C4!M`rplma{^7!CM!;9K*>pC<$ZSEY%DU(fI7@gJ&*emC)P;ioZ$(R$ zG#&Qd-)*EJ==(F%EA!$m{phppfwq_I`QMmo9Q!uiqrziKx7pT3PD^!nCN=cDKXhY5 zZUEPtTbm~OB+FLbUUsy5*`vldI}OV#X;Z727dI(##OdpNo3mt{u#Tqd%`evlDrzVC z2(@XcrEOWH!YN$3#AfR@{}o*IP8zdzw0dct3iRBdD%!ZYOEa*FPc>j!$Ru0OJIx28 zq!&mm6@KgNDzbZ7M*9l|GhoFqUsWqS`>SSpgGjskO1+<+p(0DpcLtY-M9y^!wOuvc z)O5o#u0LLG|4Qon1kO4%+`V=pT}fzhsbxZV@wU>B4H?@v_C4pkda^s@^oluWGPXvA z6>aiyV0RGz%UXWE_+e+yfgKDCOtZn~0=&<&@RwqHw1Iifxy`Mtj5P`!uhxq%cy=pY z)X%Tm-%Ra#V9yH4V6hKszkYEm`&|@yY^U}9=%f%1dqq#p-g)0OCR!44hjOah`m6FhgSD{s$Yi^!JK z2+W@_dy=8%i(WzX2G$c(t9F`4*stRF9h9JMRO}>Kxxo8->b~{BaDUSHsaW9X+C?t= zSN=(7sA6VSvu$J0SOeTTx%B(Q2e-r;PoHAhc$;yysDg}!JR>iQVBu~-O>Ostvu;1; z{c_pk78ifYPbBEVq^%mVn*KZ2D(||zOK95_jqF){t0b&O2HS=(;ei<=iozq`02-uz+t!T0QISLCh9>wjM(R{Z2jx4~4a zX3v!V4Qm&!l8nh>m=XNYB*-ABaqtvQsVZGRPN3OwV2!PC{xWt~$(696{G!U6yQ literal 0 HcmV?d00001 diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..57eb2b9 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,49 @@ +markdown_extensions: + - pymdownx.highlight: + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.details + - pymdownx.superfences + - pymdownx.mark +nav: + - Home: 'index.md' + - 'methods.md' + - 'dispatch.md' + - 'async.md' + - 'faq.md' + - 'examples.md' +repo_name: jsonrpcserver +repo_url: https://github.com/explodinglabs/jsonrpcserver +site_author: Exploding Labs +site_description: Welcome to the documentation for Jsonrcpcserver. +site_name: Jsonrpcserver +site_url: https://www.jsonrpcserver.com/ +theme: + features: + - content.code.copy + - navigation.footer + - navigation.tabs + - toc.integrate + name: material + palette: + # Palette toggle for automatic mode + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to system preference +extra: + version: + provider: mike