diff --git a/404.html b/404.html index c48cfe2..de7e16c 100644 --- a/404.html +++ b/404.html @@ -9,7 +9,7 @@ - + @@ -17,7 +17,7 @@
- + \ No newline at end of file diff --git a/assets/app.CtbirKdi.js b/assets/app.CtbirKdi.js new file mode 100644 index 0000000..a15634e --- /dev/null +++ b/assets/app.CtbirKdi.js @@ -0,0 +1 @@ +import{t as i}from"./chunks/theme.B0m-fXrT.js";import{R as o,a1 as u,a2 as c,a3 as l,a4 as f,a5 as d,a6 as m,a7 as h,a8 as g,a9 as A,aa as v,d as P,u as y,v as C,s as b,ab as w,ac as R,ad as E,ae as S}from"./chunks/framework.p2VkXzrt.js";function p(e){if(e.extends){const a=p(e.extends);return{...a,...e,async enhanceApp(t){a.enhanceApp&&await a.enhanceApp(t),e.enhanceApp&&await e.enhanceApp(t)}}}return e}const s=p(i),T=P({name:"VitePressApp",setup(){const{site:e,lang:a,dir:t}=y();return C(()=>{b(()=>{document.documentElement.lang=a.value,document.documentElement.dir=t.value})}),e.value.router.prefetchLinks&&w(),R(),E(),s.setup&&s.setup(),()=>S(s.Layout)}});async function D(){globalThis.__VITEPRESS__=!0;const e=j(),a=_();a.provide(c,e);const t=l(e.route);return a.provide(f,t),a.component("Content",d),a.component("ClientOnly",m),Object.defineProperties(a.config.globalProperties,{$frontmatter:{get(){return t.frontmatter.value}},$params:{get(){return t.page.value.params}}}),s.enhanceApp&&await s.enhanceApp({app:a,router:e,siteData:h}),{app:a,router:e,data:t}}function _(){return g(T)}function j(){let e=o,a;return A(t=>{let n=v(t),r=null;return n&&(e&&(a=n),(e||a===n)&&(n=n.replace(/\.js$/,".lean.js")),r=import(n)),o&&(e=!1),r},s.NotFound)}o&&D().then(({app:e,router:a,data:t})=>{a.go().then(()=>{u(a.route,t.site),e.mount("#app")})});export{D as createApp}; diff --git a/assets/banner-cards/flash-handler-default-implementation.png b/assets/banner-cards/flash-handler-default-implementation.png new file mode 100644 index 0000000..f11afb3 Binary files /dev/null and b/assets/banner-cards/flash-handler-default-implementation.png differ diff --git a/assets/banner-cards/flash-websockets.png b/assets/banner-cards/flash-websockets.png new file mode 100644 index 0000000..48337c4 Binary files /dev/null and b/assets/banner-cards/flash-websockets.png differ diff --git a/assets/chunks/theme.B0m-fXrT.js b/assets/chunks/theme.B0m-fXrT.js new file mode 100644 index 0000000..a0a2c63 --- /dev/null +++ b/assets/chunks/theme.B0m-fXrT.js @@ -0,0 +1 @@ +import{d as h,o as a,c as l,r as u,n as N,a as D,t as T,b as k,w as p,e as m,T as ue,_ as b,u as Ae,i as Be,f as Ce,g as de,h as g,j as d,k as i,l as q,m as re,p as M,q as O,s as Q,v as j,x as ve,y as pe,z as Ee,A as Fe,B as W,F as I,C as B,D as ge,E as X,G as _,H as F,I as $e,J as Z,K as z,L as x,M as De,N as ye,O as Oe,P as Pe,Q as Le,R as ee,S as Ge,U as Ve,V as Se,W as Ue,X as ze,Y as je,Z as qe,$ as We}from"./framework.p2VkXzrt.js";const Ke=h({__name:"VPBadge",props:{text:{},type:{default:"tip"}},setup(o){return(e,t)=>(a(),l("span",{class:N(["VPBadge",e.type])},[u(e.$slots,"default",{},()=>[D(T(e.text),1)])],2))}}),Je={key:0,class:"VPBackdrop"},Re=h({__name:"VPBackdrop",props:{show:{type:Boolean}},setup(o){return(e,t)=>(a(),k(ue,{name:"fade"},{default:p(()=>[e.show?(a(),l("div",Je)):m("",!0)]),_:1}))}}),Ye=b(Re,[["__scopeId","data-v-54a304ca"]]),P=Ae;function Qe(o,e){let t,s=!1;return()=>{t&&clearTimeout(t),s?t=setTimeout(o,e):(o(),(s=!0)&&setTimeout(()=>s=!1,e))}}function ie(o){return/^\//.test(o)?o:`/${o}`}function fe(o){const{pathname:e,search:t,hash:s,protocol:n}=new URL(o,"http://a.com");if(Be(o)||o.startsWith("#")||!n.startsWith("http")||!Ce(e))return o;const{site:r}=P(),c=e.endsWith("/")||e.endsWith(".html")?o:o.replace(/(?:(^\.+)\/)?.*$/,`$1${e.replace(/(\.md)?$/,r.value.cleanUrls?"":".html")}${t}${s}`);return de(c)}function J({correspondingLink:o=!1}={}){const{site:e,localeIndex:t,page:s,theme:n,hash:r}=P(),c=g(()=>{var v,y;return{label:(v=e.value.locales[t.value])==null?void 0:v.label,link:((y=e.value.locales[t.value])==null?void 0:y.link)||(t.value==="root"?"/":`/${t.value}/`)}});return{localeLinks:g(()=>Object.entries(e.value.locales).flatMap(([v,y])=>c.value.label===y.label?[]:{text:y.label,link:Xe(y.link||(v==="root"?"/":`/${v}/`),n.value.i18nRouting!==!1&&o,s.value.relativePath.slice(c.value.link.length-1),!e.value.cleanUrls)+r.value})),currentLang:c}}function Xe(o,e,t,s){return e?o.replace(/\/$/,"")+ie(t.replace(/(^|\/)index\.md$/,"$1").replace(/\.md$/,s?".html":"")):o}const Ze={class:"NotFound"},xe={class:"code"},et={class:"title"},tt={class:"quote"},nt={class:"action"},st=["href","aria-label"],ot=h({__name:"NotFound",setup(o){const{theme:e}=P(),{currentLang:t}=J();return(s,n)=>{var r,c,f,v,y;return a(),l("div",Ze,[d("p",xe,T(((r=i(e).notFound)==null?void 0:r.code)??"404"),1),d("h1",et,T(((c=i(e).notFound)==null?void 0:c.title)??"PAGE NOT FOUND"),1),n[0]||(n[0]=d("div",{class:"divider"},null,-1)),d("blockquote",tt,T(((f=i(e).notFound)==null?void 0:f.quote)??"But if you don't change your direction, and if you keep looking, you may end up where you are heading."),1),d("div",nt,[d("a",{class:"link",href:i(de)(i(t).link),"aria-label":((v=i(e).notFound)==null?void 0:v.linkLabel)??"go to home"},T(((y=i(e).notFound)==null?void 0:y.linkText)??"Take me home"),9,st)])])}}}),at=b(ot,[["__scopeId","data-v-6ff51ddd"]]);function Te(o,e){if(Array.isArray(o))return R(o);if(o==null)return[];e=ie(e);const t=Object.keys(o).sort((n,r)=>r.split("/").length-n.split("/").length).find(n=>e.startsWith(ie(n))),s=t?o[t]:[];return Array.isArray(s)?R(s):R(s.items,s.base)}function rt(o){const e=[];let t=0;for(const s in o){const n=o[s];if(n.items){t=e.push(n);continue}e[t]||e.push({items:[]}),e[t].items.push(n)}return e}function it(o){const e=[];function t(s){for(const n of s)n.text&&n.link&&e.push({text:n.text,link:n.link,docFooterText:n.docFooterText}),n.items&&t(n.items)}return t(o),e}function le(o,e){return Array.isArray(e)?e.some(t=>le(o,t)):q(o,e.link)?!0:e.items?le(o,e.items):!1}function R(o,e){return[...o].map(t=>{const s={...t},n=s.base||e;return n&&s.link&&(s.link=n+s.link),s.items&&(s.items=R(s.items,n)),s})}function G(){const{frontmatter:o,page:e,theme:t}=P(),s=re("(min-width: 960px)"),n=M(!1),r=g(()=>{const A=t.value.sidebar,S=e.value.relativePath;return A?Te(A,S):[]}),c=M(r.value);O(r,(A,S)=>{JSON.stringify(A)!==JSON.stringify(S)&&(c.value=r.value)});const f=g(()=>o.value.sidebar!==!1&&c.value.length>0&&o.value.layout!=="home"),v=g(()=>y?o.value.aside==null?t.value.aside==="left":o.value.aside==="left":!1),y=g(()=>o.value.layout==="home"?!1:o.value.aside!=null?!!o.value.aside:t.value.aside!==!1),L=g(()=>f.value&&s.value),$=g(()=>f.value?rt(c.value):[]);function V(){n.value=!0}function w(){n.value=!1}function H(){n.value?w():V()}return{isOpen:n,sidebar:c,sidebarGroups:$,hasSidebar:f,hasAside:y,leftAside:v,isSidebarEnabled:L,open:V,close:w,toggle:H}}function lt(o,e){let t;Q(()=>{t=o.value?document.activeElement:void 0}),j(()=>{window.addEventListener("keyup",s)}),ve(()=>{window.removeEventListener("keyup",s)});function s(n){n.key==="Escape"&&o.value&&(e(),t==null||t.focus())}}function ct(o){const{page:e,hash:t}=P(),s=M(!1),n=g(()=>o.value.collapsed!=null),r=g(()=>!!o.value.link),c=M(!1),f=()=>{c.value=q(e.value.relativePath,o.value.link)};O([e,o,t],f),j(f);const v=g(()=>c.value?!0:o.value.items?le(e.value.relativePath,o.value.items):!1),y=g(()=>!!(o.value.items&&o.value.items.length));Q(()=>{s.value=!!(n.value&&o.value.collapsed)}),pe(()=>{(c.value||v.value)&&(s.value=!1)});function L(){n.value&&(s.value=!s.value)}return{collapsed:s,collapsible:n,isLink:r,isActiveLink:c,hasActiveLink:v,hasChildren:y,toggle:L}}function ut(){const{hasSidebar:o}=G(),e=re("(min-width: 960px)"),t=re("(min-width: 1280px)");return{isAsideEnabled:g(()=>!t.value&&!e.value?!1:o.value?t.value:e.value)}}const ce=[];function Me(o){return typeof o.outline=="object"&&!Array.isArray(o.outline)&&o.outline.label||o.outlineTitle||"On this page"}function me(o){const e=[...document.querySelectorAll(".VPDoc :where(h1,h2,h3,h4,h5,h6)")].filter(t=>t.id&&t.hasChildNodes()).map(t=>{const s=Number(t.tagName[1]);return{element:t,title:dt(t),link:"#"+t.id,level:s}});return vt(e,o)}function dt(o){let e="";for(const t of o.childNodes)if(t.nodeType===1){if(t.classList.contains("VPBadge")||t.classList.contains("header-anchor")||t.classList.contains("ignore-header"))continue;e+=t.textContent}else t.nodeType===3&&(e+=t.textContent);return e.trim()}function vt(o,e){if(e===!1)return[];const t=(typeof e=="object"&&!Array.isArray(e)?e.level:e)||2,[s,n]=typeof t=="number"?[t,t]:t==="deep"?[2,6]:t;return mt(o,s,n)}function pt(o,e){const{isAsideEnabled:t}=ut(),s=Qe(r,100);let n=null;j(()=>{requestAnimationFrame(r),window.addEventListener("scroll",s)}),Ee(()=>{c(location.hash)}),ve(()=>{window.removeEventListener("scroll",s)});function r(){if(!t.value)return;const f=window.scrollY,v=window.innerHeight,y=document.body.offsetHeight,L=Math.abs(f+v-y)<1,$=ce.map(({element:w,link:H})=>({link:H,top:ft(w)})).filter(({top:w})=>!Number.isNaN(w)).sort((w,H)=>w.top-H.top);if(!$.length){c(null);return}if(f<1){c(null);return}if(L){c($[$.length-1].link);return}let V=null;for(const{link:w,top:H}of $){if(H>f+Fe()+4)break;V=w}c(V)}function c(f){n&&n.classList.remove("active"),f==null?n=null:n=o.value.querySelector(`a[href="${decodeURIComponent(f)}"]`);const v=n;v?(v.classList.add("active"),e.value.style.top=v.offsetTop+39+"px",e.value.style.opacity="1"):(e.value.style.top="33px",e.value.style.opacity="0")}}function ft(o){let e=0;for(;o!==document.body;){if(o===null)return NaN;e+=o.offsetTop,o=o.offsetParent}return e}function mt(o,e,t){ce.length=0;const s=[],n=[];return o.forEach(r=>{const c={...r,children:[]};let f=n[n.length-1];for(;f&&f.level>=c.level;)n.pop(),f=n[n.length-1];if(c.element.classList.contains("ignore-header")||f&&"shouldIgnore"in f){n.push({level:c.level,shouldIgnore:!0});return}c.level>t||c.level{const n=W("VPDocOutlineItem",!0);return a(),l("ul",{class:N(["VPDocOutlineItem",t.root?"root":"nested"])},[(a(!0),l(I,null,B(t.headers,({children:r,link:c,title:f})=>(a(),l("li",null,[d("a",{class:"outline-link",href:c,onClick:e,title:f},T(f),9,ht),r!=null&&r.length?(a(),k(n,{key:0,headers:r},null,8,["headers"])):m("",!0)]))),256))],2)}}}),Ne=b(_t,[["__scopeId","data-v-53c99d69"]]),kt={class:"content"},bt={"aria-level":"2",class:"outline-title",id:"doc-outline-aria-label",role:"heading"},gt=h({__name:"VPDocAsideOutline",setup(o){const{frontmatter:e,theme:t}=P(),s=ge([]);X(()=>{s.value=me(e.value.outline??t.value.outline)});const n=M(),r=M();return pt(n,r),(c,f)=>(a(),l("nav",{"aria-labelledby":"doc-outline-aria-label",class:N(["VPDocAsideOutline",{"has-outline":s.value.length>0}]),ref_key:"container",ref:n},[d("div",kt,[d("div",{class:"outline-marker",ref_key:"marker",ref:r},null,512),d("div",bt,T(i(Me)(i(t))),1),_(Ne,{headers:s.value,root:!0},null,8,["headers"])])],2))}}),$t=b(gt,[["__scopeId","data-v-f610f197"]]),yt={class:"VPDocAsideCarbonAds"},Pt=h({__name:"VPDocAsideCarbonAds",props:{carbonAds:{}},setup(o){const e=()=>null;return(t,s)=>(a(),l("div",yt,[_(i(e),{"carbon-ads":t.carbonAds},null,8,["carbon-ads"])]))}}),Lt={class:"VPDocAside"},Vt=h({__name:"VPDocAside",setup(o){const{theme:e}=P();return(t,s)=>(a(),l("div",Lt,[u(t.$slots,"aside-top",{},void 0,!0),u(t.$slots,"aside-outline-before",{},void 0,!0),_($t),u(t.$slots,"aside-outline-after",{},void 0,!0),s[0]||(s[0]=d("div",{class:"spacer"},null,-1)),u(t.$slots,"aside-ads-before",{},void 0,!0),i(e).carbonAds?(a(),k(Pt,{key:0,"carbon-ads":i(e).carbonAds},null,8,["carbon-ads"])):m("",!0),u(t.$slots,"aside-ads-after",{},void 0,!0),u(t.$slots,"aside-bottom",{},void 0,!0)]))}}),St=b(Vt,[["__scopeId","data-v-cb998dce"]]);function Tt(){const{theme:o,page:e}=P();return g(()=>{const{text:t="Edit this page",pattern:s=""}=o.value.editLink||{};let n;return typeof s=="function"?n=s(e.value):n=s.replace(/:path/g,e.value.filePath),{url:n,text:t}})}function Mt(){const{page:o,theme:e,frontmatter:t}=P();return g(()=>{var y,L,$,V,w,H,A,S;const s=Te(e.value.sidebar,o.value.relativePath),n=it(s),r=Nt(n,C=>C.link.replace(/[?#].*$/,"")),c=r.findIndex(C=>q(o.value.relativePath,C.link)),f=((y=e.value.docFooter)==null?void 0:y.prev)===!1&&!t.value.prev||t.value.prev===!1,v=((L=e.value.docFooter)==null?void 0:L.next)===!1&&!t.value.next||t.value.next===!1;return{prev:f?void 0:{text:(typeof t.value.prev=="string"?t.value.prev:typeof t.value.prev=="object"?t.value.prev.text:void 0)??(($=r[c-1])==null?void 0:$.docFooterText)??((V=r[c-1])==null?void 0:V.text),link:(typeof t.value.prev=="object"?t.value.prev.link:void 0)??((w=r[c-1])==null?void 0:w.link)},next:v?void 0:{text:(typeof t.value.next=="string"?t.value.next:typeof t.value.next=="object"?t.value.next.text:void 0)??((H=r[c+1])==null?void 0:H.docFooterText)??((A=r[c+1])==null?void 0:A.text),link:(typeof t.value.next=="object"?t.value.next.link:void 0)??((S=r[c+1])==null?void 0:S.link)}}})}function Nt(o,e){const t=new Set;return o.filter(s=>{const n=e(s);return t.has(n)?!1:t.add(n)})}const E=h({__name:"VPLink",props:{tag:{},href:{},noIcon:{type:Boolean},target:{},rel:{}},setup(o){const e=o,t=g(()=>e.tag??(e.href?"a":"span")),s=g(()=>e.href&&$e.test(e.href)||e.target==="_blank");return(n,r)=>(a(),k(F(t.value),{class:N(["VPLink",{link:n.href,"vp-external-link-icon":s.value,"no-icon":n.noIcon}]),href:n.href?i(fe)(n.href):void 0,target:n.target??(s.value?"_blank":void 0),rel:n.rel??(s.value?"noreferrer":void 0)},{default:p(()=>[u(n.$slots,"default")]),_:3},8,["class","href","target","rel"]))}}),It={class:"VPLastUpdated"},wt=["datetime"],Ht=h({__name:"VPDocFooterLastUpdated",setup(o){const{theme:e,page:t,lang:s}=P(),n=g(()=>new Date(t.value.lastUpdated)),r=g(()=>n.value.toISOString()),c=M("");return j(()=>{Q(()=>{var f,v,y;c.value=new Intl.DateTimeFormat((v=(f=e.value.lastUpdated)==null?void 0:f.formatOptions)!=null&&v.forceLocale?s.value:void 0,((y=e.value.lastUpdated)==null?void 0:y.formatOptions)??{dateStyle:"short",timeStyle:"short"}).format(n.value)})}),(f,v)=>{var y;return a(),l("p",It,[D(T(((y=i(e).lastUpdated)==null?void 0:y.text)||i(e).lastUpdatedText||"Last updated")+": ",1),d("time",{datetime:r.value},T(c.value),9,wt)])}}}),At=b(Ht,[["__scopeId","data-v-1bb0c8a8"]]),Bt={key:0,class:"VPDocFooter"},Ct={key:0,class:"edit-info"},Et={key:0,class:"edit-link"},Ft={key:1,class:"last-updated"},Dt={key:1,class:"prev-next","aria-labelledby":"doc-footer-aria-label"},Ot={class:"pager"},Gt=["innerHTML"],Ut=["innerHTML"],zt={class:"pager"},jt=["innerHTML"],qt=["innerHTML"],Wt=h({__name:"VPDocFooter",setup(o){const{theme:e,page:t,frontmatter:s}=P(),n=Tt(),r=Mt(),c=g(()=>e.value.editLink&&s.value.editLink!==!1),f=g(()=>t.value.lastUpdated),v=g(()=>c.value||f.value||r.value.prev||r.value.next);return(y,L)=>{var $,V,w,H;return v.value?(a(),l("footer",Bt,[u(y.$slots,"doc-footer-before",{},void 0,!0),c.value||f.value?(a(),l("div",Ct,[c.value?(a(),l("div",Et,[_(E,{class:"edit-link-button",href:i(n).url,"no-icon":!0},{default:p(()=>[L[0]||(L[0]=d("span",{class:"vpi-square-pen edit-link-icon"},null,-1)),D(" "+T(i(n).text),1)]),_:1},8,["href"])])):m("",!0),f.value?(a(),l("div",Ft,[_(At)])):m("",!0)])):m("",!0),($=i(r).prev)!=null&&$.link||(V=i(r).next)!=null&&V.link?(a(),l("nav",Dt,[L[1]||(L[1]=d("span",{class:"visually-hidden",id:"doc-footer-aria-label"},"Pager",-1)),d("div",Ot,[(w=i(r).prev)!=null&&w.link?(a(),k(E,{key:0,class:"pager-link prev",href:i(r).prev.link},{default:p(()=>{var A;return[d("span",{class:"desc",innerHTML:((A=i(e).docFooter)==null?void 0:A.prev)||"Previous page"},null,8,Gt),d("span",{class:"title",innerHTML:i(r).prev.text},null,8,Ut)]}),_:1},8,["href"])):m("",!0)]),d("div",zt,[(H=i(r).next)!=null&&H.link?(a(),k(E,{key:0,class:"pager-link next",href:i(r).next.link},{default:p(()=>{var A;return[d("span",{class:"desc",innerHTML:((A=i(e).docFooter)==null?void 0:A.next)||"Next page"},null,8,jt),d("span",{class:"title",innerHTML:i(r).next.text},null,8,qt)]}),_:1},8,["href"])):m("",!0)])])):m("",!0)])):m("",!0)}}}),Kt=b(Wt,[["__scopeId","data-v-1bcd8184"]]),Jt={class:"container"},Rt={class:"aside-container"},Yt={class:"aside-content"},Qt={class:"content"},Xt={class:"content-container"},Zt={class:"main"},xt=h({__name:"VPDoc",setup(o){const{theme:e}=P(),t=Z(),{hasSidebar:s,hasAside:n,leftAside:r}=G(),c=g(()=>t.path.replace(/[./]+/g,"_").replace(/_html$/,""));return(f,v)=>{const y=W("Content");return a(),l("div",{class:N(["VPDoc",{"has-sidebar":i(s),"has-aside":i(n)}])},[u(f.$slots,"doc-top",{},void 0,!0),d("div",Jt,[i(n)?(a(),l("div",{key:0,class:N(["aside",{"left-aside":i(r)}])},[v[0]||(v[0]=d("div",{class:"aside-curtain"},null,-1)),d("div",Rt,[d("div",Yt,[_(St,null,{"aside-top":p(()=>[u(f.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":p(()=>[u(f.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":p(()=>[u(f.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":p(()=>[u(f.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":p(()=>[u(f.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":p(()=>[u(f.$slots,"aside-ads-after",{},void 0,!0)]),_:3})])])],2)):m("",!0),d("div",Qt,[d("div",Xt,[u(f.$slots,"doc-before",{},void 0,!0),d("main",Zt,[_(y,{class:N(["vp-doc",[c.value,i(e).externalLinkIcon&&"external-link-icon-enabled"]])},null,8,["class"])]),_(Kt,null,{"doc-footer-before":p(()=>[u(f.$slots,"doc-footer-before",{},void 0,!0)]),_:3}),u(f.$slots,"doc-after",{},void 0,!0)])])]),u(f.$slots,"doc-bottom",{},void 0,!0)],2)}}}),en=b(xt,[["__scopeId","data-v-e6f2a212"]]),tn=h({__name:"VPButton",props:{tag:{},size:{default:"medium"},theme:{default:"brand"},text:{},href:{},target:{},rel:{}},setup(o){const e=o,t=g(()=>e.href&&$e.test(e.href)),s=g(()=>e.tag||(e.href?"a":"button"));return(n,r)=>(a(),k(F(s.value),{class:N(["VPButton",[n.size,n.theme]]),href:n.href?i(fe)(n.href):void 0,target:e.target??(t.value?"_blank":void 0),rel:e.rel??(t.value?"noreferrer":void 0)},{default:p(()=>[D(T(n.text),1)]),_:1},8,["class","href","target","rel"]))}}),nn=b(tn,[["__scopeId","data-v-93dc4167"]]),sn=["src","alt"],on=h({inheritAttrs:!1,__name:"VPImage",props:{image:{},alt:{}},setup(o){return(e,t)=>{const s=W("VPImage",!0);return e.image?(a(),l(I,{key:0},[typeof e.image=="string"||"src"in e.image?(a(),l("img",z({key:0,class:"VPImage"},typeof e.image=="string"?e.$attrs:{...e.image,...e.$attrs},{src:i(de)(typeof e.image=="string"?e.image:e.image.src),alt:e.alt??(typeof e.image=="string"?"":e.image.alt||"")}),null,16,sn)):(a(),l(I,{key:1},[_(s,z({class:"dark",image:e.image.dark,alt:e.image.alt},e.$attrs),null,16,["image","alt"]),_(s,z({class:"light",image:e.image.light,alt:e.image.alt},e.$attrs),null,16,["image","alt"])],64))],64)):m("",!0)}}}),Y=b(on,[["__scopeId","data-v-ab19afbb"]]),an={class:"container"},rn={class:"main"},ln={key:0,class:"name"},cn=["innerHTML"],un=["innerHTML"],dn=["innerHTML"],vn={key:0,class:"actions"},pn={key:0,class:"image"},fn={class:"image-container"},mn=h({__name:"VPHero",props:{name:{},text:{},tagline:{},image:{},actions:{}},setup(o){const e=x("hero-image-slot-exists");return(t,s)=>(a(),l("div",{class:N(["VPHero",{"has-image":t.image||i(e)}])},[d("div",an,[d("div",rn,[u(t.$slots,"home-hero-info-before",{},void 0,!0),u(t.$slots,"home-hero-info",{},()=>[t.name?(a(),l("h1",ln,[d("span",{innerHTML:t.name,class:"clip"},null,8,cn)])):m("",!0),t.text?(a(),l("p",{key:1,innerHTML:t.text,class:"text"},null,8,un)):m("",!0),t.tagline?(a(),l("p",{key:2,innerHTML:t.tagline,class:"tagline"},null,8,dn)):m("",!0)],!0),u(t.$slots,"home-hero-info-after",{},void 0,!0),t.actions?(a(),l("div",vn,[(a(!0),l(I,null,B(t.actions,n=>(a(),l("div",{key:n.link,class:"action"},[_(nn,{tag:"a",size:"medium",theme:n.theme,text:n.text,href:n.link,target:n.target,rel:n.rel},null,8,["theme","text","href","target","rel"])]))),128))])):m("",!0),u(t.$slots,"home-hero-actions-after",{},void 0,!0)]),t.image||i(e)?(a(),l("div",pn,[d("div",fn,[s[0]||(s[0]=d("div",{class:"image-bg"},null,-1)),u(t.$slots,"home-hero-image",{},()=>[t.image?(a(),k(Y,{key:0,class:"image-src",image:t.image},null,8,["image"])):m("",!0)],!0)])])):m("",!0)])],2))}}),hn=b(mn,[["__scopeId","data-v-b10c5094"]]),_n=h({__name:"VPHomeHero",setup(o){const{frontmatter:e}=P();return(t,s)=>i(e).hero?(a(),k(hn,{key:0,class:"VPHomeHero",name:i(e).hero.name,text:i(e).hero.text,tagline:i(e).hero.tagline,image:i(e).hero.image,actions:i(e).hero.actions},{"home-hero-info-before":p(()=>[u(t.$slots,"home-hero-info-before")]),"home-hero-info":p(()=>[u(t.$slots,"home-hero-info")]),"home-hero-info-after":p(()=>[u(t.$slots,"home-hero-info-after")]),"home-hero-actions-after":p(()=>[u(t.$slots,"home-hero-actions-after")]),"home-hero-image":p(()=>[u(t.$slots,"home-hero-image")]),_:3},8,["name","text","tagline","image","actions"])):m("",!0)}}),kn={class:"box"},bn={key:0,class:"icon"},gn=["innerHTML"],$n=["innerHTML"],yn=["innerHTML"],Pn={key:4,class:"link-text"},Ln={class:"link-text-value"},Vn=h({__name:"VPFeature",props:{icon:{},title:{},details:{},link:{},linkText:{},rel:{},target:{}},setup(o){return(e,t)=>(a(),k(E,{class:"VPFeature",href:e.link,rel:e.rel,target:e.target,"no-icon":!0,tag:e.link?"a":"div"},{default:p(()=>[d("article",kn,[typeof e.icon=="object"&&e.icon.wrap?(a(),l("div",bn,[_(Y,{image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])])):typeof e.icon=="object"?(a(),k(Y,{key:1,image:e.icon,alt:e.icon.alt,height:e.icon.height||48,width:e.icon.width||48},null,8,["image","alt","height","width"])):e.icon?(a(),l("div",{key:2,class:"icon",innerHTML:e.icon},null,8,gn)):m("",!0),d("h2",{class:"title",innerHTML:e.title},null,8,$n),e.details?(a(),l("p",{key:3,class:"details",innerHTML:e.details},null,8,yn)):m("",!0),e.linkText?(a(),l("div",Pn,[d("p",Ln,[D(T(e.linkText)+" ",1),t[0]||(t[0]=d("span",{class:"vpi-arrow-right link-text-icon"},null,-1))])])):m("",!0)])]),_:1},8,["href","rel","target","tag"]))}}),Sn=b(Vn,[["__scopeId","data-v-bd37d1a2"]]),Tn={key:0,class:"VPFeatures"},Mn={class:"container"},Nn={class:"items"},In=h({__name:"VPFeatures",props:{features:{}},setup(o){const e=o,t=g(()=>{const s=e.features.length;if(s){if(s===2)return"grid-2";if(s===3)return"grid-3";if(s%3===0)return"grid-6";if(s>3)return"grid-4"}else return});return(s,n)=>s.features?(a(),l("div",Tn,[d("div",Mn,[d("div",Nn,[(a(!0),l(I,null,B(s.features,r=>(a(),l("div",{key:r.title,class:N(["item",[t.value]])},[_(Sn,{icon:r.icon,title:r.title,details:r.details,link:r.link,"link-text":r.linkText,rel:r.rel,target:r.target},null,8,["icon","title","details","link","link-text","rel","target"])],2))),128))])])])):m("",!0)}}),wn=b(In,[["__scopeId","data-v-b1eea84a"]]),Hn=h({__name:"VPHomeFeatures",setup(o){const{frontmatter:e}=P();return(t,s)=>i(e).features?(a(),k(wn,{key:0,class:"VPHomeFeatures",features:i(e).features},null,8,["features"])):m("",!0)}}),An=h({__name:"VPHomeContent",setup(o){const{width:e}=De({initialWidth:0,includeScrollbar:!1});return(t,s)=>(a(),l("div",{class:"vp-doc container",style:ye(i(e)?{"--vp-offset":`calc(50% - ${i(e)/2}px)`}:{})},[u(t.$slots,"default",{},void 0,!0)],4))}}),Bn=b(An,[["__scopeId","data-v-c141a4bd"]]),Cn={class:"VPHome"},En=h({__name:"VPHome",setup(o){const{frontmatter:e}=P();return(t,s)=>{const n=W("Content");return a(),l("div",Cn,[u(t.$slots,"home-hero-before",{},void 0,!0),_(_n,null,{"home-hero-info-before":p(()=>[u(t.$slots,"home-hero-info-before",{},void 0,!0)]),"home-hero-info":p(()=>[u(t.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-info-after":p(()=>[u(t.$slots,"home-hero-info-after",{},void 0,!0)]),"home-hero-actions-after":p(()=>[u(t.$slots,"home-hero-actions-after",{},void 0,!0)]),"home-hero-image":p(()=>[u(t.$slots,"home-hero-image",{},void 0,!0)]),_:3}),u(t.$slots,"home-hero-after",{},void 0,!0),u(t.$slots,"home-features-before",{},void 0,!0),_(Hn),u(t.$slots,"home-features-after",{},void 0,!0),i(e).markdownStyles!==!1?(a(),k(Bn,{key:0},{default:p(()=>[_(n)]),_:1})):(a(),k(n,{key:1}))])}}}),Fn=b(En,[["__scopeId","data-v-07b1ad08"]]),Dn={},On={class:"VPPage"};function Gn(o,e){const t=W("Content");return a(),l("div",On,[u(o.$slots,"page-top"),_(t),u(o.$slots,"page-bottom")])}const Un=b(Dn,[["render",Gn]]),zn=h({__name:"VPContent",setup(o){const{page:e,frontmatter:t}=P(),{hasSidebar:s}=G();return(n,r)=>(a(),l("div",{class:N(["VPContent",{"has-sidebar":i(s),"is-home":i(t).layout==="home"}]),id:"VPContent"},[i(e).isNotFound?u(n.$slots,"not-found",{key:0},()=>[_(at)],!0):i(t).layout==="page"?(a(),k(Un,{key:1},{"page-top":p(()=>[u(n.$slots,"page-top",{},void 0,!0)]),"page-bottom":p(()=>[u(n.$slots,"page-bottom",{},void 0,!0)]),_:3})):i(t).layout==="home"?(a(),k(Fn,{key:2},{"home-hero-before":p(()=>[u(n.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info-before":p(()=>[u(n.$slots,"home-hero-info-before",{},void 0,!0)]),"home-hero-info":p(()=>[u(n.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-info-after":p(()=>[u(n.$slots,"home-hero-info-after",{},void 0,!0)]),"home-hero-actions-after":p(()=>[u(n.$slots,"home-hero-actions-after",{},void 0,!0)]),"home-hero-image":p(()=>[u(n.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":p(()=>[u(n.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":p(()=>[u(n.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":p(()=>[u(n.$slots,"home-features-after",{},void 0,!0)]),_:3})):i(t).layout&&i(t).layout!=="doc"?(a(),k(F(i(t).layout),{key:3})):(a(),k(en,{key:4},{"doc-top":p(()=>[u(n.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":p(()=>[u(n.$slots,"doc-bottom",{},void 0,!0)]),"doc-footer-before":p(()=>[u(n.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":p(()=>[u(n.$slots,"doc-before",{},void 0,!0)]),"doc-after":p(()=>[u(n.$slots,"doc-after",{},void 0,!0)]),"aside-top":p(()=>[u(n.$slots,"aside-top",{},void 0,!0)]),"aside-outline-before":p(()=>[u(n.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":p(()=>[u(n.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":p(()=>[u(n.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":p(()=>[u(n.$slots,"aside-ads-after",{},void 0,!0)]),"aside-bottom":p(()=>[u(n.$slots,"aside-bottom",{},void 0,!0)]),_:3}))],2))}}),jn=b(zn,[["__scopeId","data-v-9a6c75ad"]]),qn={class:"container"},Wn=["innerHTML"],Kn=["innerHTML"],Jn=h({__name:"VPFooter",setup(o){const{theme:e,frontmatter:t}=P(),{hasSidebar:s}=G();return(n,r)=>i(e).footer&&i(t).footer!==!1?(a(),l("footer",{key:0,class:N(["VPFooter",{"has-sidebar":i(s)}])},[d("div",qn,[i(e).footer.message?(a(),l("p",{key:0,class:"message",innerHTML:i(e).footer.message},null,8,Wn)):m("",!0),i(e).footer.copyright?(a(),l("p",{key:1,class:"copyright",innerHTML:i(e).footer.copyright},null,8,Kn)):m("",!0)])],2)):m("",!0)}}),Rn=b(Jn,[["__scopeId","data-v-566314d4"]]);function Yn(){const{theme:o,frontmatter:e}=P(),t=ge([]),s=g(()=>t.value.length>0);return X(()=>{t.value=me(e.value.outline??o.value.outline)}),{headers:t,hasLocalNav:s}}const Qn={class:"menu-text"},Xn={class:"header"},Zn={class:"outline"},xn=h({__name:"VPLocalNavOutlineDropdown",props:{headers:{},navHeight:{}},setup(o){const e=o,{theme:t}=P(),s=M(!1),n=M(0),r=M(),c=M();function f($){var V;(V=r.value)!=null&&V.contains($.target)||(s.value=!1)}O(s,$=>{if($){document.addEventListener("click",f);return}document.removeEventListener("click",f)}),Oe("Escape",()=>{s.value=!1}),X(()=>{s.value=!1});function v(){s.value=!s.value,n.value=window.innerHeight+Math.min(window.scrollY-e.navHeight,0)}function y($){$.target.classList.contains("outline-link")&&(c.value&&(c.value.style.transition="none"),Pe(()=>{s.value=!1}))}function L(){s.value=!1,window.scrollTo({top:0,left:0,behavior:"smooth"})}return($,V)=>(a(),l("div",{class:"VPLocalNavOutlineDropdown",style:ye({"--vp-vh":n.value+"px"}),ref_key:"main",ref:r},[$.headers.length>0?(a(),l("button",{key:0,onClick:v,class:N({open:s.value})},[d("span",Qn,T(i(Me)(i(t))),1),V[0]||(V[0]=d("span",{class:"vpi-chevron-right icon"},null,-1))],2)):(a(),l("button",{key:1,onClick:L},T(i(t).returnToTopLabel||"Return to top"),1)),_(ue,{name:"flyout"},{default:p(()=>[s.value?(a(),l("div",{key:0,ref_key:"items",ref:c,class:"items",onClick:y},[d("div",Xn,[d("a",{class:"top-link",href:"#",onClick:L},T(i(t).returnToTopLabel||"Return to top"),1)]),d("div",Zn,[_(Ne,{headers:$.headers},null,8,["headers"])])],512)):m("",!0)]),_:1})],4))}}),es=b(xn,[["__scopeId","data-v-883964e0"]]),ts={class:"container"},ns=["aria-expanded"],ss={class:"menu-text"},os=h({__name:"VPLocalNav",props:{open:{type:Boolean}},emits:["open-menu"],setup(o){const{theme:e,frontmatter:t}=P(),{hasSidebar:s}=G(),{headers:n}=Yn(),{y:r}=Le(),c=M(0);j(()=>{c.value=parseInt(getComputedStyle(document.documentElement).getPropertyValue("--vp-nav-height"))}),X(()=>{n.value=me(t.value.outline??e.value.outline)});const f=g(()=>n.value.length===0),v=g(()=>f.value&&!s.value),y=g(()=>({VPLocalNav:!0,"has-sidebar":s.value,empty:f.value,fixed:v.value}));return(L,$)=>i(t).layout!=="home"&&(!v.value||i(r)>=c.value)?(a(),l("div",{key:0,class:N(y.value)},[d("div",ts,[i(s)?(a(),l("button",{key:0,class:"menu","aria-expanded":L.open,"aria-controls":"VPSidebarNav",onClick:$[0]||($[0]=V=>L.$emit("open-menu"))},[$[1]||($[1]=d("span",{class:"vpi-align-left menu-icon"},null,-1)),d("span",ss,T(i(e).sidebarMenuLabel||"Menu"),1)],8,ns)):m("",!0),_(es,{headers:i(n),navHeight:c.value},null,8,["headers","navHeight"])])],2)):m("",!0)}}),as=b(os,[["__scopeId","data-v-2488c25a"]]);function rs(){const o=M(!1);function e(){o.value=!0,window.addEventListener("resize",n)}function t(){o.value=!1,window.removeEventListener("resize",n)}function s(){o.value?t():e()}function n(){window.outerWidth>=768&&t()}const r=Z();return O(()=>r.path,t),{isScreenOpen:o,openScreen:e,closeScreen:t,toggleScreen:s}}const is={},ls={class:"VPSwitch",type:"button",role:"switch"},cs={class:"check"},us={key:0,class:"icon"};function ds(o,e){return a(),l("button",ls,[d("span",cs,[o.$slots.default?(a(),l("span",us,[u(o.$slots,"default",{},void 0,!0)])):m("",!0)])])}const vs=b(is,[["render",ds],["__scopeId","data-v-b4ccac88"]]),ps=h({__name:"VPSwitchAppearance",setup(o){const{isDark:e,theme:t}=P(),s=x("toggle-appearance",()=>{e.value=!e.value}),n=M("");return pe(()=>{n.value=e.value?t.value.lightModeSwitchTitle||"Switch to light theme":t.value.darkModeSwitchTitle||"Switch to dark theme"}),(r,c)=>(a(),k(vs,{title:n.value,class:"VPSwitchAppearance","aria-checked":i(e),onClick:i(s)},{default:p(()=>c[0]||(c[0]=[d("span",{class:"vpi-sun sun"},null,-1),d("span",{class:"vpi-moon moon"},null,-1)])),_:1},8,["title","aria-checked","onClick"]))}}),he=b(ps,[["__scopeId","data-v-be9742d9"]]),fs={key:0,class:"VPNavBarAppearance"},ms=h({__name:"VPNavBarAppearance",setup(o){const{site:e}=P();return(t,s)=>i(e).appearance&&i(e).appearance!=="force-dark"&&i(e).appearance!=="force-auto"?(a(),l("div",fs,[_(he)])):m("",!0)}}),hs=b(ms,[["__scopeId","data-v-3f90c1a5"]]),_e=M();let Ie=!1,ae=0;function _s(o){const e=M(!1);if(ee){!Ie&&ks(),ae++;const t=O(_e,s=>{var n,r,c;s===o.el.value||(n=o.el.value)!=null&&n.contains(s)?(e.value=!0,(r=o.onFocus)==null||r.call(o)):(e.value=!1,(c=o.onBlur)==null||c.call(o))});ve(()=>{t(),ae--,ae||bs()})}return Ge(e)}function ks(){document.addEventListener("focusin",we),Ie=!0,_e.value=document.activeElement}function bs(){document.removeEventListener("focusin",we)}function we(){_e.value=document.activeElement}const gs={class:"VPMenuLink"},$s=["innerHTML"],ys=h({__name:"VPMenuLink",props:{item:{}},setup(o){const{page:e}=P();return(t,s)=>(a(),l("div",gs,[_(E,{class:N({active:i(q)(i(e).relativePath,t.item.activeMatch||t.item.link,!!t.item.activeMatch)}),href:t.item.link,target:t.item.target,rel:t.item.rel,"no-icon":t.item.noIcon},{default:p(()=>[d("span",{innerHTML:t.item.text},null,8,$s)]),_:1},8,["class","href","target","rel","no-icon"])]))}}),te=b(ys,[["__scopeId","data-v-7eeeb2dc"]]),Ps={class:"VPMenuGroup"},Ls={key:0,class:"title"},Vs=h({__name:"VPMenuGroup",props:{text:{},items:{}},setup(o){return(e,t)=>(a(),l("div",Ps,[e.text?(a(),l("p",Ls,T(e.text),1)):m("",!0),(a(!0),l(I,null,B(e.items,s=>(a(),l(I,null,["link"in s?(a(),k(te,{key:0,item:s},null,8,["item"])):m("",!0)],64))),256))]))}}),Ss=b(Vs,[["__scopeId","data-v-a6b0397c"]]),Ts={class:"VPMenu"},Ms={key:0,class:"items"},Ns=h({__name:"VPMenu",props:{items:{}},setup(o){return(e,t)=>(a(),l("div",Ts,[e.items?(a(),l("div",Ms,[(a(!0),l(I,null,B(e.items,s=>(a(),l(I,{key:JSON.stringify(s)},["link"in s?(a(),k(te,{key:0,item:s},null,8,["item"])):"component"in s?(a(),k(F(s.component),z({key:1,ref_for:!0},s.props),null,16)):(a(),k(Ss,{key:2,text:s.text,items:s.items},null,8,["text","items"]))],64))),128))])):m("",!0),u(e.$slots,"default",{},void 0,!0)]))}}),Is=b(Ns,[["__scopeId","data-v-20ed86d6"]]),ws=["aria-expanded","aria-label"],Hs={key:0,class:"text"},As=["innerHTML"],Bs={key:1,class:"vpi-more-horizontal icon"},Cs={class:"menu"},Es=h({__name:"VPFlyout",props:{icon:{},button:{},label:{},items:{}},setup(o){const e=M(!1),t=M();_s({el:t,onBlur:s});function s(){e.value=!1}return(n,r)=>(a(),l("div",{class:"VPFlyout",ref_key:"el",ref:t,onMouseenter:r[1]||(r[1]=c=>e.value=!0),onMouseleave:r[2]||(r[2]=c=>e.value=!1)},[d("button",{type:"button",class:"button","aria-haspopup":"true","aria-expanded":e.value,"aria-label":n.label,onClick:r[0]||(r[0]=c=>e.value=!e.value)},[n.button||n.icon?(a(),l("span",Hs,[n.icon?(a(),l("span",{key:0,class:N([n.icon,"option-icon"])},null,2)):m("",!0),n.button?(a(),l("span",{key:1,innerHTML:n.button},null,8,As)):m("",!0),r[3]||(r[3]=d("span",{class:"vpi-chevron-down text-icon"},null,-1))])):(a(),l("span",Bs))],8,ws),d("div",Cs,[_(Is,{items:n.items},{default:p(()=>[u(n.$slots,"default",{},void 0,!0)]),_:3},8,["items"])])],544))}}),ke=b(Es,[["__scopeId","data-v-bfe7971f"]]),Fs=["href","aria-label","innerHTML"],Ds=h({__name:"VPSocialLink",props:{icon:{},link:{},ariaLabel:{}},setup(o){const e=o,t=M();j(async()=>{var r;await Pe();const n=(r=t.value)==null?void 0:r.children[0];n instanceof HTMLElement&&n.className.startsWith("vpi-social-")&&(getComputedStyle(n).maskImage||getComputedStyle(n).webkitMaskImage)==="none"&&n.style.setProperty("--icon",`url('https://api.iconify.design/simple-icons/${e.icon}.svg')`)});const s=g(()=>typeof e.icon=="object"?e.icon.svg:``);return(n,r)=>(a(),l("a",{ref_key:"el",ref:t,class:"VPSocialLink no-icon",href:n.link,"aria-label":n.ariaLabel??(typeof n.icon=="string"?n.icon:""),target:"_blank",rel:"noopener",innerHTML:s.value},null,8,Fs))}}),Os=b(Ds,[["__scopeId","data-v-60a9a2d3"]]),Gs={class:"VPSocialLinks"},Us=h({__name:"VPSocialLinks",props:{links:{}},setup(o){return(e,t)=>(a(),l("div",Gs,[(a(!0),l(I,null,B(e.links,({link:s,icon:n,ariaLabel:r})=>(a(),k(Os,{key:s,icon:n,link:s,ariaLabel:r},null,8,["icon","link","ariaLabel"]))),128))]))}}),ne=b(Us,[["__scopeId","data-v-e71e869c"]]),zs={key:0,class:"group translations"},js={class:"trans-title"},qs={key:1,class:"group"},Ws={class:"item appearance"},Ks={class:"label"},Js={class:"appearance-action"},Rs={key:2,class:"group"},Ys={class:"item social-links"},Qs=h({__name:"VPNavBarExtra",setup(o){const{site:e,theme:t}=P(),{localeLinks:s,currentLang:n}=J({correspondingLink:!0}),r=g(()=>s.value.length&&n.value.label||e.value.appearance||t.value.socialLinks);return(c,f)=>r.value?(a(),k(ke,{key:0,class:"VPNavBarExtra",label:"extra navigation"},{default:p(()=>[i(s).length&&i(n).label?(a(),l("div",zs,[d("p",js,T(i(n).label),1),(a(!0),l(I,null,B(i(s),v=>(a(),k(te,{key:v.link,item:v},null,8,["item"]))),128))])):m("",!0),i(e).appearance&&i(e).appearance!=="force-dark"&&i(e).appearance!=="force-auto"?(a(),l("div",qs,[d("div",Ws,[d("p",Ks,T(i(t).darkModeSwitchLabel||"Appearance"),1),d("div",Js,[_(he)])])])):m("",!0),i(t).socialLinks?(a(),l("div",Rs,[d("div",Ys,[_(ne,{class:"social-links-list",links:i(t).socialLinks},null,8,["links"])])])):m("",!0)]),_:1})):m("",!0)}}),Xs=b(Qs,[["__scopeId","data-v-f953d92f"]]),Zs=["aria-expanded"],xs=h({__name:"VPNavBarHamburger",props:{active:{type:Boolean}},emits:["click"],setup(o){return(e,t)=>(a(),l("button",{type:"button",class:N(["VPNavBarHamburger",{active:e.active}]),"aria-label":"mobile navigation","aria-expanded":e.active,"aria-controls":"VPNavScreen",onClick:t[0]||(t[0]=s=>e.$emit("click"))},t[1]||(t[1]=[d("span",{class:"container"},[d("span",{class:"top"}),d("span",{class:"middle"}),d("span",{class:"bottom"})],-1)]),10,Zs))}}),eo=b(xs,[["__scopeId","data-v-6bee1efd"]]),to=["innerHTML"],no=h({__name:"VPNavBarMenuLink",props:{item:{}},setup(o){const{page:e}=P();return(t,s)=>(a(),k(E,{class:N({VPNavBarMenuLink:!0,active:i(q)(i(e).relativePath,t.item.activeMatch||t.item.link,!!t.item.activeMatch)}),href:t.item.link,target:t.item.target,rel:t.item.rel,"no-icon":t.item.noIcon,tabindex:"0"},{default:p(()=>[d("span",{innerHTML:t.item.text},null,8,to)]),_:1},8,["class","href","target","rel","no-icon"]))}}),so=b(no,[["__scopeId","data-v-815115f5"]]),oo=h({__name:"VPNavBarMenuGroup",props:{item:{}},setup(o){const e=o,{page:t}=P(),s=r=>"component"in r?!1:"link"in r?q(t.value.relativePath,r.link,!!e.item.activeMatch):r.items.some(s),n=g(()=>s(e.item));return(r,c)=>(a(),k(ke,{class:N({VPNavBarMenuGroup:!0,active:i(q)(i(t).relativePath,r.item.activeMatch,!!r.item.activeMatch)||n.value}),button:r.item.text,items:r.item.items},null,8,["class","button","items"]))}}),ao={key:0,"aria-labelledby":"main-nav-aria-label",class:"VPNavBarMenu"},ro=h({__name:"VPNavBarMenu",setup(o){const{theme:e}=P();return(t,s)=>i(e).nav?(a(),l("nav",ao,[s[0]||(s[0]=d("span",{id:"main-nav-aria-label",class:"visually-hidden"}," Main Navigation ",-1)),(a(!0),l(I,null,B(i(e).nav,n=>(a(),l(I,{key:JSON.stringify(n)},["link"in n?(a(),k(so,{key:0,item:n},null,8,["item"])):"component"in n?(a(),k(F(n.component),z({key:1,ref_for:!0},n.props),null,16)):(a(),k(oo,{key:2,item:n},null,8,["item"]))],64))),128))])):m("",!0)}}),io=b(ro,[["__scopeId","data-v-afb2845e"]]);function lo(o){const{localeIndex:e,theme:t}=P();function s(n){var H,A,S;const r=n.split("."),c=(H=t.value.search)==null?void 0:H.options,f=c&&typeof c=="object",v=f&&((S=(A=c.locales)==null?void 0:A[e.value])==null?void 0:S.translations)||null,y=f&&c.translations||null;let L=v,$=y,V=o;const w=r.pop();for(const C of r){let U=null;const K=V==null?void 0:V[C];K&&(U=V=K);const se=$==null?void 0:$[C];se&&(U=$=se);const oe=L==null?void 0:L[C];oe&&(U=L=oe),K||(V=U),se||($=U),oe||(L=U)}return(L==null?void 0:L[w])??($==null?void 0:$[w])??(V==null?void 0:V[w])??""}return s}const co=["aria-label"],uo={class:"DocSearch-Button-Container"},vo={class:"DocSearch-Button-Placeholder"},be=h({__name:"VPNavBarSearchButton",setup(o){const t=lo({button:{buttonText:"Search",buttonAriaLabel:"Search"}});return(s,n)=>(a(),l("button",{type:"button",class:"DocSearch DocSearch-Button","aria-label":i(t)("button.buttonAriaLabel")},[d("span",uo,[n[0]||(n[0]=d("span",{class:"vp-icon DocSearch-Search-Icon"},null,-1)),d("span",vo,T(i(t)("button.buttonText")),1)]),n[1]||(n[1]=d("span",{class:"DocSearch-Button-Keys"},[d("kbd",{class:"DocSearch-Button-Key"}),d("kbd",{class:"DocSearch-Button-Key"},"K")],-1))],8,co))}}),po={class:"VPNavBarSearch"},fo={id:"local-search"},mo={key:1,id:"docsearch"},ho=h({__name:"VPNavBarSearch",setup(o){const e=()=>null,t=()=>null,{theme:s}=P(),n=M(!1),r=M(!1);j(()=>{});function c(){n.value||(n.value=!0,setTimeout(f,16))}function f(){const L=new Event("keydown");L.key="k",L.metaKey=!0,window.dispatchEvent(L),setTimeout(()=>{document.querySelector(".DocSearch-Modal")||f()},16)}const v=M(!1),y="";return(L,$)=>{var V;return a(),l("div",po,[i(y)==="local"?(a(),l(I,{key:0},[v.value?(a(),k(i(e),{key:0,onClose:$[0]||($[0]=w=>v.value=!1)})):m("",!0),d("div",fo,[_(be,{onClick:$[1]||($[1]=w=>v.value=!0)})])],64)):i(y)==="algolia"?(a(),l(I,{key:1},[n.value?(a(),k(i(t),{key:0,algolia:((V=i(s).search)==null?void 0:V.options)??i(s).algolia,onVnodeBeforeMount:$[2]||($[2]=w=>r.value=!0)},null,8,["algolia"])):m("",!0),r.value?m("",!0):(a(),l("div",mo,[_(be,{onClick:c})]))],64)):m("",!0)])}}}),_o=h({__name:"VPNavBarSocialLinks",setup(o){const{theme:e}=P();return(t,s)=>i(e).socialLinks?(a(),k(ne,{key:0,class:"VPNavBarSocialLinks",links:i(e).socialLinks},null,8,["links"])):m("",!0)}}),ko=b(_o,[["__scopeId","data-v-ef6192dc"]]),bo=["href","rel","target"],go=["innerHTML"],$o={key:2},yo=h({__name:"VPNavBarTitle",setup(o){const{site:e,theme:t}=P(),{hasSidebar:s}=G(),{currentLang:n}=J(),r=g(()=>{var v;return typeof t.value.logoLink=="string"?t.value.logoLink:(v=t.value.logoLink)==null?void 0:v.link}),c=g(()=>{var v;return typeof t.value.logoLink=="string"||(v=t.value.logoLink)==null?void 0:v.rel}),f=g(()=>{var v;return typeof t.value.logoLink=="string"||(v=t.value.logoLink)==null?void 0:v.target});return(v,y)=>(a(),l("div",{class:N(["VPNavBarTitle",{"has-sidebar":i(s)}])},[d("a",{class:"title",href:r.value??i(fe)(i(n).link),rel:c.value,target:f.value},[u(v.$slots,"nav-bar-title-before",{},void 0,!0),i(t).logo?(a(),k(Y,{key:0,class:"logo",image:i(t).logo},null,8,["image"])):m("",!0),i(t).siteTitle?(a(),l("span",{key:1,innerHTML:i(t).siteTitle},null,8,go)):i(t).siteTitle===void 0?(a(),l("span",$o,T(i(e).title),1)):m("",!0),u(v.$slots,"nav-bar-title-after",{},void 0,!0)],8,bo)],2))}}),Po=b(yo,[["__scopeId","data-v-9f43907a"]]),Lo={class:"items"},Vo={class:"title"},So=h({__name:"VPNavBarTranslations",setup(o){const{theme:e}=P(),{localeLinks:t,currentLang:s}=J({correspondingLink:!0});return(n,r)=>i(t).length&&i(s).label?(a(),k(ke,{key:0,class:"VPNavBarTranslations",icon:"vpi-languages",label:i(e).langMenuLabel||"Change language"},{default:p(()=>[d("div",Lo,[d("p",Vo,T(i(s).label),1),(a(!0),l(I,null,B(i(t),c=>(a(),k(te,{key:c.link,item:c},null,8,["item"]))),128))])]),_:1},8,["label"])):m("",!0)}}),To=b(So,[["__scopeId","data-v-acee064b"]]),Mo={class:"wrapper"},No={class:"container"},Io={class:"title"},wo={class:"content"},Ho={class:"content-body"},Ao=h({__name:"VPNavBar",props:{isScreenOpen:{type:Boolean}},emits:["toggle-screen"],setup(o){const e=o,{y:t}=Le(),{hasSidebar:s}=G(),{frontmatter:n}=P(),r=M({});return pe(()=>{r.value={"has-sidebar":s.value,home:n.value.layout==="home",top:t.value===0,"screen-open":e.isScreenOpen}}),(c,f)=>(a(),l("div",{class:N(["VPNavBar",r.value])},[d("div",Mo,[d("div",No,[d("div",Io,[_(Po,null,{"nav-bar-title-before":p(()=>[u(c.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":p(()=>[u(c.$slots,"nav-bar-title-after",{},void 0,!0)]),_:3})]),d("div",wo,[d("div",Ho,[u(c.$slots,"nav-bar-content-before",{},void 0,!0),_(ho,{class:"search"}),_(io,{class:"menu"}),_(To,{class:"translations"}),_(hs,{class:"appearance"}),_(ko,{class:"social-links"}),_(Xs,{class:"extra"}),u(c.$slots,"nav-bar-content-after",{},void 0,!0),_(eo,{class:"hamburger",active:c.isScreenOpen,onClick:f[0]||(f[0]=v=>c.$emit("toggle-screen"))},null,8,["active"])])])])]),f[1]||(f[1]=d("div",{class:"divider"},[d("div",{class:"divider-line"})],-1))],2))}}),Bo=b(Ao,[["__scopeId","data-v-9fd4d1dd"]]),Co={key:0,class:"VPNavScreenAppearance"},Eo={class:"text"},Fo=h({__name:"VPNavScreenAppearance",setup(o){const{site:e,theme:t}=P();return(s,n)=>i(e).appearance&&i(e).appearance!=="force-dark"&&i(e).appearance!=="force-auto"?(a(),l("div",Co,[d("p",Eo,T(i(t).darkModeSwitchLabel||"Appearance"),1),_(he)])):m("",!0)}}),Do=b(Fo,[["__scopeId","data-v-a3e2920d"]]),Oo=["innerHTML"],Go=h({__name:"VPNavScreenMenuLink",props:{item:{}},setup(o){const e=x("close-screen");return(t,s)=>(a(),k(E,{class:"VPNavScreenMenuLink",href:t.item.link,target:t.item.target,rel:t.item.rel,"no-icon":t.item.noIcon,onClick:i(e)},{default:p(()=>[d("span",{innerHTML:t.item.text},null,8,Oo)]),_:1},8,["href","target","rel","no-icon","onClick"]))}}),Uo=b(Go,[["__scopeId","data-v-fa963d97"]]),zo=["innerHTML"],jo=h({__name:"VPNavScreenMenuGroupLink",props:{item:{}},setup(o){const e=x("close-screen");return(t,s)=>(a(),k(E,{class:"VPNavScreenMenuGroupLink",href:t.item.link,target:t.item.target,rel:t.item.rel,"no-icon":t.item.noIcon,onClick:i(e)},{default:p(()=>[d("span",{innerHTML:t.item.text},null,8,zo)]),_:1},8,["href","target","rel","no-icon","onClick"]))}}),He=b(jo,[["__scopeId","data-v-e04f3e85"]]),qo={class:"VPNavScreenMenuGroupSection"},Wo={key:0,class:"title"},Ko=h({__name:"VPNavScreenMenuGroupSection",props:{text:{},items:{}},setup(o){return(e,t)=>(a(),l("div",qo,[e.text?(a(),l("p",Wo,T(e.text),1)):m("",!0),(a(!0),l(I,null,B(e.items,s=>(a(),k(He,{key:s.text,item:s},null,8,["item"]))),128))]))}}),Jo=b(Ko,[["__scopeId","data-v-f60dbfa7"]]),Ro=["aria-controls","aria-expanded"],Yo=["innerHTML"],Qo=["id"],Xo={key:0,class:"item"},Zo={key:1,class:"item"},xo={key:2,class:"group"},ea=h({__name:"VPNavScreenMenuGroup",props:{text:{},items:{}},setup(o){const e=o,t=M(!1),s=g(()=>`NavScreenGroup-${e.text.replace(" ","-").toLowerCase()}`);function n(){t.value=!t.value}return(r,c)=>(a(),l("div",{class:N(["VPNavScreenMenuGroup",{open:t.value}])},[d("button",{class:"button","aria-controls":s.value,"aria-expanded":t.value,onClick:n},[d("span",{class:"button-text",innerHTML:r.text},null,8,Yo),c[0]||(c[0]=d("span",{class:"vpi-plus button-icon"},null,-1))],8,Ro),d("div",{id:s.value,class:"items"},[(a(!0),l(I,null,B(r.items,f=>(a(),l(I,{key:JSON.stringify(f)},["link"in f?(a(),l("div",Xo,[_(He,{item:f},null,8,["item"])])):"component"in f?(a(),l("div",Zo,[(a(),k(F(f.component),z({ref_for:!0},f.props,{"screen-menu":""}),null,16))])):(a(),l("div",xo,[_(Jo,{text:f.text,items:f.items},null,8,["text","items"])]))],64))),128))],8,Qo)],2))}}),ta=b(ea,[["__scopeId","data-v-d99bfeec"]]),na={key:0,class:"VPNavScreenMenu"},sa=h({__name:"VPNavScreenMenu",setup(o){const{theme:e}=P();return(t,s)=>i(e).nav?(a(),l("nav",na,[(a(!0),l(I,null,B(i(e).nav,n=>(a(),l(I,{key:JSON.stringify(n)},["link"in n?(a(),k(Uo,{key:0,item:n},null,8,["item"])):"component"in n?(a(),k(F(n.component),z({key:1,ref_for:!0},n.props,{"screen-menu":""}),null,16)):(a(),k(ta,{key:2,text:n.text||"",items:n.items},null,8,["text","items"]))],64))),128))])):m("",!0)}}),oa=h({__name:"VPNavScreenSocialLinks",setup(o){const{theme:e}=P();return(t,s)=>i(e).socialLinks?(a(),k(ne,{key:0,class:"VPNavScreenSocialLinks",links:i(e).socialLinks},null,8,["links"])):m("",!0)}}),aa={class:"list"},ra=h({__name:"VPNavScreenTranslations",setup(o){const{localeLinks:e,currentLang:t}=J({correspondingLink:!0}),s=M(!1);function n(){s.value=!s.value}return(r,c)=>i(e).length&&i(t).label?(a(),l("div",{key:0,class:N(["VPNavScreenTranslations",{open:s.value}])},[d("button",{class:"title",onClick:n},[c[0]||(c[0]=d("span",{class:"vpi-languages icon lang"},null,-1)),D(" "+T(i(t).label)+" ",1),c[1]||(c[1]=d("span",{class:"vpi-chevron-down icon chevron"},null,-1))]),d("ul",aa,[(a(!0),l(I,null,B(i(e),f=>(a(),l("li",{key:f.link,class:"item"},[_(E,{class:"link",href:f.link},{default:p(()=>[D(T(f.text),1)]),_:2},1032,["href"])]))),128))])],2)):m("",!0)}}),ia=b(ra,[["__scopeId","data-v-516e4bc3"]]),la={class:"container"},ca=h({__name:"VPNavScreen",props:{open:{type:Boolean}},setup(o){const e=M(null),t=Ve(ee?document.body:null);return(s,n)=>(a(),k(ue,{name:"fade",onEnter:n[0]||(n[0]=r=>t.value=!0),onAfterLeave:n[1]||(n[1]=r=>t.value=!1)},{default:p(()=>[s.open?(a(),l("div",{key:0,class:"VPNavScreen",ref_key:"screen",ref:e,id:"VPNavScreen"},[d("div",la,[u(s.$slots,"nav-screen-content-before",{},void 0,!0),_(sa,{class:"menu"}),_(ia,{class:"translations"}),_(Do,{class:"appearance"}),_(oa,{class:"social-links"}),u(s.$slots,"nav-screen-content-after",{},void 0,!0)])],512)):m("",!0)]),_:3}))}}),ua=b(ca,[["__scopeId","data-v-2dd6d0c7"]]),da={key:0,class:"VPNav"},va=h({__name:"VPNav",setup(o){const{isScreenOpen:e,closeScreen:t,toggleScreen:s}=rs(),{frontmatter:n}=P(),r=g(()=>n.value.navbar!==!1);return Se("close-screen",t),Q(()=>{ee&&document.documentElement.classList.toggle("hide-nav",!r.value)}),(c,f)=>r.value?(a(),l("header",da,[_(Bo,{"is-screen-open":i(e),onToggleScreen:i(s)},{"nav-bar-title-before":p(()=>[u(c.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":p(()=>[u(c.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":p(()=>[u(c.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":p(()=>[u(c.$slots,"nav-bar-content-after",{},void 0,!0)]),_:3},8,["is-screen-open","onToggleScreen"]),_(ua,{open:i(e)},{"nav-screen-content-before":p(()=>[u(c.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":p(()=>[u(c.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3},8,["open"])])):m("",!0)}}),pa=b(va,[["__scopeId","data-v-7ad780c2"]]),fa=["role","tabindex"],ma={key:1,class:"items"},ha=h({__name:"VPSidebarItem",props:{item:{},depth:{}},setup(o){const e=o,{collapsed:t,collapsible:s,isLink:n,isActiveLink:r,hasActiveLink:c,hasChildren:f,toggle:v}=ct(g(()=>e.item)),y=g(()=>f.value?"section":"div"),L=g(()=>n.value?"a":"div"),$=g(()=>f.value?e.depth+2===7?"p":`h${e.depth+2}`:"p"),V=g(()=>n.value?void 0:"button"),w=g(()=>[[`level-${e.depth}`],{collapsible:s.value},{collapsed:t.value},{"is-link":n.value},{"is-active":r.value},{"has-active":c.value}]);function H(S){"key"in S&&S.key!=="Enter"||!e.item.link&&v()}function A(){e.item.link&&v()}return(S,C)=>{const U=W("VPSidebarItem",!0);return a(),k(F(y.value),{class:N(["VPSidebarItem",w.value])},{default:p(()=>[S.item.text?(a(),l("div",z({key:0,class:"item",role:V.value},ze(S.item.items?{click:H,keydown:H}:{},!0),{tabindex:S.item.items&&0}),[C[1]||(C[1]=d("div",{class:"indicator"},null,-1)),S.item.link?(a(),k(E,{key:0,tag:L.value,class:"link",href:S.item.link,rel:S.item.rel,target:S.item.target},{default:p(()=>[(a(),k(F($.value),{class:"text",innerHTML:S.item.text},null,8,["innerHTML"]))]),_:1},8,["tag","href","rel","target"])):(a(),k(F($.value),{key:1,class:"text",innerHTML:S.item.text},null,8,["innerHTML"])),S.item.collapsed!=null&&S.item.items&&S.item.items.length?(a(),l("div",{key:2,class:"caret",role:"button","aria-label":"toggle section",onClick:A,onKeydown:Ue(A,["enter"]),tabindex:"0"},C[0]||(C[0]=[d("span",{class:"vpi-chevron-right caret-icon"},null,-1)]),32)):m("",!0)],16,fa)):m("",!0),S.item.items&&S.item.items.length?(a(),l("div",ma,[S.depth<5?(a(!0),l(I,{key:0},B(S.item.items,K=>(a(),k(U,{key:K.text,item:K,depth:S.depth+1},null,8,["item","depth"]))),128)):m("",!0)])):m("",!0)]),_:1},8,["class"])}}}),_a=b(ha,[["__scopeId","data-v-edd2eed8"]]),ka=h({__name:"VPSidebarGroup",props:{items:{}},setup(o){const e=M(!0);let t=null;return j(()=>{t=setTimeout(()=>{t=null,e.value=!1},300)}),je(()=>{t!=null&&(clearTimeout(t),t=null)}),(s,n)=>(a(!0),l(I,null,B(s.items,r=>(a(),l("div",{key:r.text,class:N(["group",{"no-transition":e.value}])},[_(_a,{item:r,depth:0},null,8,["item"])],2))),128))}}),ba=b(ka,[["__scopeId","data-v-51288d80"]]),ga={class:"nav",id:"VPSidebarNav","aria-labelledby":"sidebar-aria-label",tabindex:"-1"},$a=h({__name:"VPSidebar",props:{open:{type:Boolean}},setup(o){const{sidebarGroups:e,hasSidebar:t}=G(),s=o,n=M(null),r=Ve(ee?document.body:null);O([s,n],()=>{var f;s.open?(r.value=!0,(f=n.value)==null||f.focus()):r.value=!1},{immediate:!0,flush:"post"});const c=M(0);return O(e,()=>{c.value+=1},{deep:!0}),(f,v)=>i(t)?(a(),l("aside",{key:0,class:N(["VPSidebar",{open:f.open}]),ref_key:"navEl",ref:n,onClick:v[0]||(v[0]=qe(()=>{},["stop"]))},[v[2]||(v[2]=d("div",{class:"curtain"},null,-1)),d("nav",ga,[v[1]||(v[1]=d("span",{class:"visually-hidden",id:"sidebar-aria-label"}," Sidebar Navigation ",-1)),u(f.$slots,"sidebar-nav-before",{},void 0,!0),(a(),k(ba,{items:i(e),key:c.value},null,8,["items"])),u(f.$slots,"sidebar-nav-after",{},void 0,!0)])],2)):m("",!0)}}),ya=b($a,[["__scopeId","data-v-42c4c606"]]),Pa=h({__name:"VPSkipLink",setup(o){const e=Z(),t=M();O(()=>e.path,()=>t.value.focus());function s({target:n}){const r=document.getElementById(decodeURIComponent(n.hash).slice(1));if(r){const c=()=>{r.removeAttribute("tabindex"),r.removeEventListener("blur",c)};r.setAttribute("tabindex","-1"),r.addEventListener("blur",c),r.focus(),window.scrollTo(0,0)}}return(n,r)=>(a(),l(I,null,[d("span",{ref_key:"backToTop",ref:t,tabindex:"-1"},null,512),d("a",{href:"#VPContent",class:"VPSkipLink visually-hidden",onClick:s}," Skip to content ")],64))}}),La=b(Pa,[["__scopeId","data-v-c8291ffa"]]),Va=h({__name:"Layout",setup(o){const{isOpen:e,open:t,close:s}=G(),n=Z();O(()=>n.path,s),lt(e,s);const{frontmatter:r}=P(),c=We(),f=g(()=>!!c["home-hero-image"]);return Se("hero-image-slot-exists",f),(v,y)=>{const L=W("Content");return i(r).layout!==!1?(a(),l("div",{key:0,class:N(["Layout",i(r).pageClass])},[u(v.$slots,"layout-top",{},void 0,!0),_(La),_(Ye,{class:"backdrop",show:i(e),onClick:i(s)},null,8,["show","onClick"]),_(pa,null,{"nav-bar-title-before":p(()=>[u(v.$slots,"nav-bar-title-before",{},void 0,!0)]),"nav-bar-title-after":p(()=>[u(v.$slots,"nav-bar-title-after",{},void 0,!0)]),"nav-bar-content-before":p(()=>[u(v.$slots,"nav-bar-content-before",{},void 0,!0)]),"nav-bar-content-after":p(()=>[u(v.$slots,"nav-bar-content-after",{},void 0,!0)]),"nav-screen-content-before":p(()=>[u(v.$slots,"nav-screen-content-before",{},void 0,!0)]),"nav-screen-content-after":p(()=>[u(v.$slots,"nav-screen-content-after",{},void 0,!0)]),_:3}),_(as,{open:i(e),onOpenMenu:i(t)},null,8,["open","onOpenMenu"]),_(ya,{open:i(e)},{"sidebar-nav-before":p(()=>[u(v.$slots,"sidebar-nav-before",{},void 0,!0)]),"sidebar-nav-after":p(()=>[u(v.$slots,"sidebar-nav-after",{},void 0,!0)]),_:3},8,["open"]),_(jn,null,{"page-top":p(()=>[u(v.$slots,"page-top",{},void 0,!0)]),"page-bottom":p(()=>[u(v.$slots,"page-bottom",{},void 0,!0)]),"not-found":p(()=>[u(v.$slots,"not-found",{},void 0,!0)]),"home-hero-before":p(()=>[u(v.$slots,"home-hero-before",{},void 0,!0)]),"home-hero-info-before":p(()=>[u(v.$slots,"home-hero-info-before",{},void 0,!0)]),"home-hero-info":p(()=>[u(v.$slots,"home-hero-info",{},void 0,!0)]),"home-hero-info-after":p(()=>[u(v.$slots,"home-hero-info-after",{},void 0,!0)]),"home-hero-actions-after":p(()=>[u(v.$slots,"home-hero-actions-after",{},void 0,!0)]),"home-hero-image":p(()=>[u(v.$slots,"home-hero-image",{},void 0,!0)]),"home-hero-after":p(()=>[u(v.$slots,"home-hero-after",{},void 0,!0)]),"home-features-before":p(()=>[u(v.$slots,"home-features-before",{},void 0,!0)]),"home-features-after":p(()=>[u(v.$slots,"home-features-after",{},void 0,!0)]),"doc-footer-before":p(()=>[u(v.$slots,"doc-footer-before",{},void 0,!0)]),"doc-before":p(()=>[u(v.$slots,"doc-before",{},void 0,!0)]),"doc-after":p(()=>[u(v.$slots,"doc-after",{},void 0,!0)]),"doc-top":p(()=>[u(v.$slots,"doc-top",{},void 0,!0)]),"doc-bottom":p(()=>[u(v.$slots,"doc-bottom",{},void 0,!0)]),"aside-top":p(()=>[u(v.$slots,"aside-top",{},void 0,!0)]),"aside-bottom":p(()=>[u(v.$slots,"aside-bottom",{},void 0,!0)]),"aside-outline-before":p(()=>[u(v.$slots,"aside-outline-before",{},void 0,!0)]),"aside-outline-after":p(()=>[u(v.$slots,"aside-outline-after",{},void 0,!0)]),"aside-ads-before":p(()=>[u(v.$slots,"aside-ads-before",{},void 0,!0)]),"aside-ads-after":p(()=>[u(v.$slots,"aside-ads-after",{},void 0,!0)]),_:3}),_(Rn),u(v.$slots,"layout-bottom",{},void 0,!0)],2)):(a(),k(L,{key:1}))}}}),Sa=b(Va,[["__scopeId","data-v-d8b57b2d"]]),Ta={class:"profile"},Ma={class:"avatar"},Na=["src","alt"],Ia={class:"data"},wa={class:"name"},Ha={key:0,class:"affiliation"},Aa={key:0,class:"title"},Ba={key:1,class:"at"},Ca=["innerHTML"],Ea={key:2,class:"links"},Fa={key:0,class:"sp"},Da=h({__name:"VPTeamMembersItem",props:{size:{default:"medium"},member:{}},setup(o){return(e,t)=>(a(),l("article",{class:N(["VPTeamMembersItem",[e.size]])},[d("div",Ta,[d("figure",Ma,[d("img",{class:"avatar-img",src:e.member.avatar,alt:e.member.name},null,8,Na)]),d("div",Ia,[d("h1",wa,T(e.member.name),1),e.member.title||e.member.org?(a(),l("p",Ha,[e.member.title?(a(),l("span",Aa,T(e.member.title),1)):m("",!0),e.member.title&&e.member.org?(a(),l("span",Ba," @ ")):m("",!0),e.member.org?(a(),k(E,{key:2,class:N(["org",{link:e.member.orgLink}]),href:e.member.orgLink,"no-icon":""},{default:p(()=>[D(T(e.member.org),1)]),_:1},8,["class","href"])):m("",!0)])):m("",!0),e.member.desc?(a(),l("p",{key:1,class:"desc",innerHTML:e.member.desc},null,8,Ca)):m("",!0),e.member.links?(a(),l("div",Ea,[_(ne,{links:e.member.links},null,8,["links"])])):m("",!0)])]),e.member.sponsor?(a(),l("div",Fa,[_(E,{class:"sp-link",href:e.member.sponsor,"no-icon":""},{default:p(()=>[t[0]||(t[0]=d("span",{class:"vpi-heart sp-icon"},null,-1)),D(" "+T(e.member.actionText||"Sponsor"),1)]),_:1},8,["href"])])):m("",!0)],2))}}),Oa=b(Da,[["__scopeId","data-v-acff304e"]]),Ga={class:"container"},Ua=h({__name:"VPTeamMembers",props:{size:{default:"medium"},members:{}},setup(o){const e=o,t=g(()=>[e.size,`count-${e.members.length}`]);return(s,n)=>(a(),l("div",{class:N(["VPTeamMembers",t.value])},[d("div",Ga,[(a(!0),l(I,null,B(s.members,r=>(a(),l("div",{key:r.name,class:"item"},[_(Oa,{size:s.size,member:r},null,8,["size","member"])]))),128))])],2))}}),ja=b(Ua,[["__scopeId","data-v-bf782009"]]),qa={Layout:Sa,enhanceApp:({app:o})=>{o.component("Badge",Ke)}};export{ja as V,qa as t}; diff --git a/assets/flash_handler-default-implementation.md.CL5VF6xH.js b/assets/flash_handler-default-implementation.md.CL5VF6xH.js new file mode 100644 index 0000000..59cab85 --- /dev/null +++ b/assets/flash_handler-default-implementation.md.CL5VF6xH.js @@ -0,0 +1,95 @@ +import{_ as i,c as a,a0 as n,o as t}from"./chunks/framework.p2VkXzrt.js";const g=JSON.parse(`{"title":"⚡ Handler Default Implementations (HDI)","description":"","frontmatter":{"banner_title":"Flash - Handler Default Implementations (HDI)","banner_description":"Leverage HDI's for cleaner and more maintainable route logic.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-handler-default-implementation.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-handler-default-implementation.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/handler-default-implementation.md","filePath":"flash/handler-default-implementation.md"}`),h={name:"flash/handler-default-implementation.md"};function l(k,s,e,p,r,d){return t(),a("div",null,s[0]||(s[0]=[n(`

⚡ Handler Default Implementations (HDI)

HDI's provide an elegant and reliable way to standardize common behaviors across multiple request handlers. By defining base handlers that extend RequestHandler (or even chaining multiple base handlers), you can modularize your logic for aspects like authentication, user data retrieval, and rate limiting.

🔗 How It Works

Instead of implementing repeated logic in every handler, you create abstract handler classes that encapsulate common functionality. Your actual route handlers then extend these base classes, inheriting their behavior while focusing purely on request-specific logic.

🛠 Example: API Key Authentication

Imagine you want to protect API endpoints with an authentication key and track API usage in a database. You can create an abstract base handler that enforces API key validation:

java
public abstract class APIKeyProtectedHandler extends RequestHandler {
+    protected String apiKey;
+    protected int remainingQuota;
+
+    public APIKeyProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        apiKey = req.header("X-API-Key");
+
+        if (apiKey == null || !isValidApiKey(apiKey)) {
+            res.status(403);
+            return "{\\"error\\":\\"Invalid API Key\\"}";
+        }
+
+        remainingQuota = getRemainingQuota(apiKey);
+        if (remainingQuota <= 0) {
+            res.status(429);
+            return "{\\"error\\":\\"API quota exceeded\\"}";
+        }
+
+        return handleAuthorized();
+    }
+
+    protected abstract Object handleAuthorized();
+
+    private boolean isValidApiKey(String key) {
+        // Implement key validation logic, e.g., checking against a database
+        return true;
+    }
+
+    private int getRemainingQuota(String key) {
+        // Retrieve remaining quota from a database
+        return 100;
+    }
+}

Now, your actual API handlers only need to extend APIKeyProtectedHandler, ensuring every request has a valid API key and available quota before executing its logic:

java
@RouteInfo(method = HttpMethod.GET, path = "/data")
+public class GetDataHandler extends APIKeyProtectedHandler {
+    public GetDataHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthorized() {
+        return "{\\"data\\":\\"Your API response here\\"}";
+    }
+}

🏗️ Chaining HDIs for Modular Logic

HDIs can be chained together to compose multiple layers of behavior. For example:

java
public abstract class ProtectedHandler extends RequestHandler {
+protected String authToken;
+
+    public ProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        authToken = req.header("Authorization");
+        if (authToken == null || !isValidToken(authToken)) {
+            res.status(401);
+            res.type("application/json");
+            return "{\\"error\\":\\"Unauthorized\\"}";
+        }
+        return handleAuthenticated();
+    }
+
+    protected abstract Object handleAuthenticated();
+}
java
public abstract class AuthenticatedHandler extends ProtectedHandler {
+    protected User user;
+
+    public AuthenticatedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthenticated() {
+        user = getUserFromDatabase(authToken);
+        if (user == null) {
+            res.status(403);
+            return "{\\"error\\":\\"User not found\\"}";
+        }
+        return handleWithUser();
+    }
+
+    protected abstract Object handleWithUser();
+}
java
@RouteInfo(method = HttpMethod.GET, path = "/profile")
+public class UserProfileHandler extends AuthenticatedHandler {
+    public UserProfileHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleWithUser() {
+        return "{\\"username\\":\\"" + user.getUsername() + "\\"}";
+    }
+}
`,17)]))}const y=i(h,[["render",l]]);export{g as __pageData,y as default}; diff --git a/assets/flash_handler-default-implementation.md.CL5VF6xH.lean.js b/assets/flash_handler-default-implementation.md.CL5VF6xH.lean.js new file mode 100644 index 0000000..59cab85 --- /dev/null +++ b/assets/flash_handler-default-implementation.md.CL5VF6xH.lean.js @@ -0,0 +1,95 @@ +import{_ as i,c as a,a0 as n,o as t}from"./chunks/framework.p2VkXzrt.js";const g=JSON.parse(`{"title":"⚡ Handler Default Implementations (HDI)","description":"","frontmatter":{"banner_title":"Flash - Handler Default Implementations (HDI)","banner_description":"Leverage HDI's for cleaner and more maintainable route logic.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-handler-default-implementation.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-handler-default-implementation.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/handler-default-implementation.md","filePath":"flash/handler-default-implementation.md"}`),h={name:"flash/handler-default-implementation.md"};function l(k,s,e,p,r,d){return t(),a("div",null,s[0]||(s[0]=[n(`

⚡ Handler Default Implementations (HDI)

HDI's provide an elegant and reliable way to standardize common behaviors across multiple request handlers. By defining base handlers that extend RequestHandler (or even chaining multiple base handlers), you can modularize your logic for aspects like authentication, user data retrieval, and rate limiting.

🔗 How It Works

Instead of implementing repeated logic in every handler, you create abstract handler classes that encapsulate common functionality. Your actual route handlers then extend these base classes, inheriting their behavior while focusing purely on request-specific logic.

🛠 Example: API Key Authentication

Imagine you want to protect API endpoints with an authentication key and track API usage in a database. You can create an abstract base handler that enforces API key validation:

java
public abstract class APIKeyProtectedHandler extends RequestHandler {
+    protected String apiKey;
+    protected int remainingQuota;
+
+    public APIKeyProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        apiKey = req.header("X-API-Key");
+
+        if (apiKey == null || !isValidApiKey(apiKey)) {
+            res.status(403);
+            return "{\\"error\\":\\"Invalid API Key\\"}";
+        }
+
+        remainingQuota = getRemainingQuota(apiKey);
+        if (remainingQuota <= 0) {
+            res.status(429);
+            return "{\\"error\\":\\"API quota exceeded\\"}";
+        }
+
+        return handleAuthorized();
+    }
+
+    protected abstract Object handleAuthorized();
+
+    private boolean isValidApiKey(String key) {
+        // Implement key validation logic, e.g., checking against a database
+        return true;
+    }
+
+    private int getRemainingQuota(String key) {
+        // Retrieve remaining quota from a database
+        return 100;
+    }
+}

Now, your actual API handlers only need to extend APIKeyProtectedHandler, ensuring every request has a valid API key and available quota before executing its logic:

java
@RouteInfo(method = HttpMethod.GET, path = "/data")
+public class GetDataHandler extends APIKeyProtectedHandler {
+    public GetDataHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthorized() {
+        return "{\\"data\\":\\"Your API response here\\"}";
+    }
+}

🏗️ Chaining HDIs for Modular Logic

HDIs can be chained together to compose multiple layers of behavior. For example:

java
public abstract class ProtectedHandler extends RequestHandler {
+protected String authToken;
+
+    public ProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        authToken = req.header("Authorization");
+        if (authToken == null || !isValidToken(authToken)) {
+            res.status(401);
+            res.type("application/json");
+            return "{\\"error\\":\\"Unauthorized\\"}";
+        }
+        return handleAuthenticated();
+    }
+
+    protected abstract Object handleAuthenticated();
+}
java
public abstract class AuthenticatedHandler extends ProtectedHandler {
+    protected User user;
+
+    public AuthenticatedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthenticated() {
+        user = getUserFromDatabase(authToken);
+        if (user == null) {
+            res.status(403);
+            return "{\\"error\\":\\"User not found\\"}";
+        }
+        return handleWithUser();
+    }
+
+    protected abstract Object handleWithUser();
+}
java
@RouteInfo(method = HttpMethod.GET, path = "/profile")
+public class UserProfileHandler extends AuthenticatedHandler {
+    public UserProfileHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleWithUser() {
+        return "{\\"username\\":\\"" + user.getUsername() + "\\"}";
+    }
+}
`,17)]))}const y=i(h,[["render",l]]);export{g as __pageData,y as default}; diff --git a/assets/flash_index.md.E5zPbZtg.js b/assets/flash_index.md.E5zPbZtg.js new file mode 100644 index 0000000..1517a93 --- /dev/null +++ b/assets/flash_index.md.E5zPbZtg.js @@ -0,0 +1 @@ +import{V as s}from"./chunks/theme.B0m-fXrT.js";import{c as i,j as t,a,G as r,k as l,o as m}from"./chunks/framework.p2VkXzrt.js";const u=JSON.parse('{"title":"⚡ Flash","description":"","frontmatter":{"next":{"text":"Installation","link":"/flash/installation"},"banner_title":"Flash - A Modern Web Framework","banner_description":"Flash is a fast and expressive web framework written in Java.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-index.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-index.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/index.md","filePath":"flash/index.md"}'),o={name:"flash/index.md"},b=Object.assign(o,{setup(d){const n=[{avatar:"https://avatars.githubusercontent.com/u/83401018?v=4",name:"Relism",title:"Developer",links:[{icon:"github",link:"https://github.com/Relism"}]},{avatar:"https://avatars.githubusercontent.com/u/69807609?v=4",name:"Sieadev",title:"Developer",links:[{icon:"github",link:"https://github.com/sieadev"}]}];return(h,e)=>(m(),i("div",null,[e[0]||(e[0]=t("h1",{id:"⚡-flash",tabindex:"-1"},[a("⚡ Flash "),t("a",{class:"header-anchor",href:"#⚡-flash","aria-label":'Permalink to "⚡ Flash"'},"​")],-1)),e[1]||(e[1]=t("p",null,"Flash is a simple, modern and fast expressive web framework written in Java. The project is maintained by Pixel Services and the open-source community.",-1)),e[2]||(e[2]=t("h1",{id:"our-team",tabindex:"-1"},[a("Our Team "),t("a",{class:"header-anchor",href:"#our-team","aria-label":'Permalink to "Our Team"'},"​")],-1)),e[3]||(e[3]=t("p",null,"Say hello to the Pixel Services team !",-1)),r(l(s),{size:"small",members:n})]))}});export{u as __pageData,b as default}; diff --git a/assets/flash_index.md.E5zPbZtg.lean.js b/assets/flash_index.md.E5zPbZtg.lean.js new file mode 100644 index 0000000..1517a93 --- /dev/null +++ b/assets/flash_index.md.E5zPbZtg.lean.js @@ -0,0 +1 @@ +import{V as s}from"./chunks/theme.B0m-fXrT.js";import{c as i,j as t,a,G as r,k as l,o as m}from"./chunks/framework.p2VkXzrt.js";const u=JSON.parse('{"title":"⚡ Flash","description":"","frontmatter":{"next":{"text":"Installation","link":"/flash/installation"},"banner_title":"Flash - A Modern Web Framework","banner_description":"Flash is a fast and expressive web framework written in Java.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-index.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-index.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/index.md","filePath":"flash/index.md"}'),o={name:"flash/index.md"},b=Object.assign(o,{setup(d){const n=[{avatar:"https://avatars.githubusercontent.com/u/83401018?v=4",name:"Relism",title:"Developer",links:[{icon:"github",link:"https://github.com/Relism"}]},{avatar:"https://avatars.githubusercontent.com/u/69807609?v=4",name:"Sieadev",title:"Developer",links:[{icon:"github",link:"https://github.com/sieadev"}]}];return(h,e)=>(m(),i("div",null,[e[0]||(e[0]=t("h1",{id:"⚡-flash",tabindex:"-1"},[a("⚡ Flash "),t("a",{class:"header-anchor",href:"#⚡-flash","aria-label":'Permalink to "⚡ Flash"'},"​")],-1)),e[1]||(e[1]=t("p",null,"Flash is a simple, modern and fast expressive web framework written in Java. The project is maintained by Pixel Services and the open-source community.",-1)),e[2]||(e[2]=t("h1",{id:"our-team",tabindex:"-1"},[a("Our Team "),t("a",{class:"header-anchor",href:"#our-team","aria-label":'Permalink to "Our Team"'},"​")],-1)),e[3]||(e[3]=t("p",null,"Say hello to the Pixel Services team !",-1)),r(l(s),{size:"small",members:n})]))}});export{u as __pageData,b as default}; diff --git a/assets/flash_installation.md.6Deie__C.js b/assets/flash_installation.md.6Deie__C.js new file mode 100644 index 0000000..0dfce6d --- /dev/null +++ b/assets/flash_installation.md.6Deie__C.js @@ -0,0 +1,19 @@ +import{p as l,v as p,c as h,a0 as r,j as e,a as k,t as E,o}from"./chunks/framework.p2VkXzrt.js";const d={href:"https://maven.pixel-services.com/#/releases/com/pixelservices/flash",style:{"text-decoration":"underline",color:"#007bff"}},m=JSON.parse('{"title":"📲 Installation","description":"","frontmatter":{"banner_title":"Flash - Installation","banner_description":"A guide on how to install the Flash library in your project.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-installation.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-installation.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/installation.md","filePath":"flash/installation.md"}'),g={name:"flash/installation.md"},v=Object.assign(g,{setup(c){const i=l("");return p(async()=>{try{const n=(await(await fetch("https://maven.pixel-services.com/api/maven/details/releases/com/pixelservices/flash")).json()).files.filter(t=>t.type==="DIRECTORY").map(t=>t.name);i.value=n.sort().pop()}catch(a){console.error("Error fetching latest version:",a),i.value="Error fetching version"}}),(a,s)=>(o(),h("div",null,[s[1]||(s[1]=r(`

📲 Installation

This page provides installation instructions for the latest version of the flash library from Pixel Services.

Installation

Maven (pom.xml)

  1. Add the repository :

    xml
    <repositories>
    +  <repository>
    +    <id>pixel-services</id>
    +    <name>Pixel Services</name>
    +    <url>https://maven.pixel-services.com/repository</url>
    +  </repository>
    +</repositories>
  2. And the dependency :

    xml
    <dependencies>
    +  <dependency>
    +    <groupId>com.pixelservices</groupId>
    +    <artifactId>flash</artifactId>
    +    <version>{{ latestVersion }}</version>
    +  </dependency>
    +</dependencies>

Gradle (build.gradle)

  1. Add the repository :

    groovy
    repositories {
    +    maven {
    +        url "https://maven.pixel-services.com/repository"
    +    }
    +}
  2. And the dependency :

    groovy
    dependencies {
    +    implementation 'com.pixelservices:flash:{{ latestVersion }}'
    +}
`,7)),e("div",null,[s[0]||(s[0]=k(" ⚡ Latest version: ")),e("a",d,[e("strong",null,E(i.value),1)])])]))}});export{m as __pageData,v as default}; diff --git a/assets/flash_installation.md.6Deie__C.lean.js b/assets/flash_installation.md.6Deie__C.lean.js new file mode 100644 index 0000000..0dfce6d --- /dev/null +++ b/assets/flash_installation.md.6Deie__C.lean.js @@ -0,0 +1,19 @@ +import{p as l,v as p,c as h,a0 as r,j as e,a as k,t as E,o}from"./chunks/framework.p2VkXzrt.js";const d={href:"https://maven.pixel-services.com/#/releases/com/pixelservices/flash",style:{"text-decoration":"underline",color:"#007bff"}},m=JSON.parse('{"title":"📲 Installation","description":"","frontmatter":{"banner_title":"Flash - Installation","banner_description":"A guide on how to install the Flash library in your project.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-installation.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-installation.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/installation.md","filePath":"flash/installation.md"}'),g={name:"flash/installation.md"},v=Object.assign(g,{setup(c){const i=l("");return p(async()=>{try{const n=(await(await fetch("https://maven.pixel-services.com/api/maven/details/releases/com/pixelservices/flash")).json()).files.filter(t=>t.type==="DIRECTORY").map(t=>t.name);i.value=n.sort().pop()}catch(a){console.error("Error fetching latest version:",a),i.value="Error fetching version"}}),(a,s)=>(o(),h("div",null,[s[1]||(s[1]=r(`

📲 Installation

This page provides installation instructions for the latest version of the flash library from Pixel Services.

Installation

Maven (pom.xml)

  1. Add the repository :

    xml
    <repositories>
    +  <repository>
    +    <id>pixel-services</id>
    +    <name>Pixel Services</name>
    +    <url>https://maven.pixel-services.com/repository</url>
    +  </repository>
    +</repositories>
  2. And the dependency :

    xml
    <dependencies>
    +  <dependency>
    +    <groupId>com.pixelservices</groupId>
    +    <artifactId>flash</artifactId>
    +    <version>{{ latestVersion }}</version>
    +  </dependency>
    +</dependencies>

Gradle (build.gradle)

  1. Add the repository :

    groovy
    repositories {
    +    maven {
    +        url "https://maven.pixel-services.com/repository"
    +    }
    +}
  2. And the dependency :

    groovy
    dependencies {
    +    implementation 'com.pixelservices:flash:{{ latestVersion }}'
    +}
`,7)),e("div",null,[s[0]||(s[0]=k(" ⚡ Latest version: ")),e("a",d,[e("strong",null,E(i.value),1)])])]))}});export{m as __pageData,v as default}; diff --git a/assets/flash_request-handler.md.D-YKsT8g.js b/assets/flash_request-handler.md.D-YKsT8g.js new file mode 100644 index 0000000..0da2583 --- /dev/null +++ b/assets/flash_request-handler.md.D-YKsT8g.js @@ -0,0 +1,12 @@ +import{_ as s,c as t,a0 as a,o as i}from"./chunks/framework.p2VkXzrt.js";const k=JSON.parse('{"title":"⚙️ Request Handler","description":"","frontmatter":{"banner_title":"Flash - Request Handler","banner_description":"Learn how to create and manage Request Handlers in Flash.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-request-handler.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-request-handler.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/request-handler.md","filePath":"flash/request-handler.md"}'),n={name:"flash/request-handler.md"};function d(h,e,r,l,o,p){return i(),t("div",null,e[0]||(e[0]=[a(`

⚙️ Request Handler

In this section, we illustrate the powerful concept of RequestHandler in Flash, which are used to handle incoming requests and generate responses.

Creating a Request Handler

To create a custom request handler, you need to extend the RequestHandler class and annotate the class with the RouteInfo annotation, specifying the HTTP method that the handler will respond to and the relative path that the handler will be registered to. After that, you must override the handle method; The handle method is where you define the logic for processing the request and generating the response. The req (request) and res (response) objects are available in the handler to access the request data and send the response back to the client.

You must call the super constructor with the req and res objects to initialize the handler.

java
@RouteInfo(method = HttpMethod.GET, path = "/hello")
+public class MyHandler extends RequestHandler {
+    public MyHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        String response = "Hello, world!";
+        return response;
+    }
+}

WARNING

Any logic that needs to be executed before the request handler is registered must be done within the constructor.

Request Handler methods

The RequestHandler class provides several methods that can be used to interact with the request and response objects easily and safely. Following are listed the methods available in the RequestHandler class, with a brief description of their purpose:

MethodParamsDescription
getRequestBody()noneReturns a JSONObject representation of the request body.
getSpecification()noneReturns an instance of HandlerSpecification containing all sorts of information about the handler.
expectedRequestParameter()String name, descriptionReturns an instance of ExpectedRequestParameter for the specified parameter name.
expectedBodyField()String name, descriptionReturns an instance of ExpectedBodyField for the specified field name.
expectedBodyFile()String name, descriptionReturns an instance of ExpectedBodyFile for the specified file name.

(More on the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile classes in the next section).

`,11)]))}const u=s(n,[["render",d]]);export{k as __pageData,u as default}; diff --git a/assets/flash_request-handler.md.D-YKsT8g.lean.js b/assets/flash_request-handler.md.D-YKsT8g.lean.js new file mode 100644 index 0000000..0da2583 --- /dev/null +++ b/assets/flash_request-handler.md.D-YKsT8g.lean.js @@ -0,0 +1,12 @@ +import{_ as s,c as t,a0 as a,o as i}from"./chunks/framework.p2VkXzrt.js";const k=JSON.parse('{"title":"⚙️ Request Handler","description":"","frontmatter":{"banner_title":"Flash - Request Handler","banner_description":"Learn how to create and manage Request Handlers in Flash.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-request-handler.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-request-handler.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/request-handler.md","filePath":"flash/request-handler.md"}'),n={name:"flash/request-handler.md"};function d(h,e,r,l,o,p){return i(),t("div",null,e[0]||(e[0]=[a(`

⚙️ Request Handler

In this section, we illustrate the powerful concept of RequestHandler in Flash, which are used to handle incoming requests and generate responses.

Creating a Request Handler

To create a custom request handler, you need to extend the RequestHandler class and annotate the class with the RouteInfo annotation, specifying the HTTP method that the handler will respond to and the relative path that the handler will be registered to. After that, you must override the handle method; The handle method is where you define the logic for processing the request and generating the response. The req (request) and res (response) objects are available in the handler to access the request data and send the response back to the client.

You must call the super constructor with the req and res objects to initialize the handler.

java
@RouteInfo(method = HttpMethod.GET, path = "/hello")
+public class MyHandler extends RequestHandler {
+    public MyHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        String response = "Hello, world!";
+        return response;
+    }
+}

WARNING

Any logic that needs to be executed before the request handler is registered must be done within the constructor.

Request Handler methods

The RequestHandler class provides several methods that can be used to interact with the request and response objects easily and safely. Following are listed the methods available in the RequestHandler class, with a brief description of their purpose:

MethodParamsDescription
getRequestBody()noneReturns a JSONObject representation of the request body.
getSpecification()noneReturns an instance of HandlerSpecification containing all sorts of information about the handler.
expectedRequestParameter()String name, descriptionReturns an instance of ExpectedRequestParameter for the specified parameter name.
expectedBodyField()String name, descriptionReturns an instance of ExpectedBodyField for the specified field name.
expectedBodyFile()String name, descriptionReturns an instance of ExpectedBodyFile for the specified file name.

(More on the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile classes in the next section).

`,11)]))}const u=s(n,[["render",d]]);export{k as __pageData,u as default}; diff --git a/assets/flash_request-response.md.AHTHQW77.js b/assets/flash_request-response.md.AHTHQW77.js new file mode 100644 index 0000000..762c32c --- /dev/null +++ b/assets/flash_request-response.md.AHTHQW77.js @@ -0,0 +1,67 @@ +import{_ as i,c as e,a0 as a,o as t}from"./chunks/framework.p2VkXzrt.js";const E=JSON.parse('{"title":"📥 Request and Response","description":"","frontmatter":{"banner_title":"Flash - Request and Response","banner_description":"Unlock the power of the Request and Response objects in Flash.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-request-response.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-request-response.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/request-response.md","filePath":"flash/request-response.md"}'),n={name:"flash/request-response.md"};function h(l,s,p,k,d,r){return t(),e("div",null,s[0]||(s[0]=[a(`

📥 Request and Response

Generally speaking, in an HTTP request-response cycle, the client sends a request to the server, and the server generates a response based on the request. In this section, we illustrate the Request and Response objects in Flash, which are used to interact with the request data and generate responses. We will learn to read and interpret the request data, and model the correct response to send back to the client.

Request and Response Objects

The Request and Response objects are passed to the handler constructor and provide access to the request data and response methods. Under the hood, these objects are provided by the RouteController during the registration stage of the handler, and they are continously updated for each request on that specific handler's instance, but for now, you can consider them as magic.

These objects are a very powerful tool to interact with the lifecycle of the server's response, and they provide a wide range of methods that makes our lives as developers easier.

RequestHandler context

Inside a RequestHandler class, you can access the Request and Response objects inside the handle method by simply typing req and res, respectively. You can use the methods provided by these objects both outside of a handler, and inside of it, to interact with the request and response objects. Although, the advantages of being inside a handler are that Flash supports out-of-the-box methods that can significantly clean up your code and improve the overall readability of your handlers.

Specifically, the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile objects are used to get the expected properties of the request, and they are used by Flash to validate the request data before the handle method is even executed.

The developer can then simply assume that all the expected parameters are present and valid, without having to write a single line of validation code: Flash will do it for you.

The three objects mentioned above are fairly similar in their usage, providing getter methods which safely return the data in the expected format and type, thanks to Flash's built-in validation and casting system.

NOTE

Flash will take care of informing the client of any missing or invalid parameters, parameters that are not in the expected format, or any other kind of error that might occur during the validation process.

REMEMBER

The ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile instances are ONLY supposed to be retrieved by calling the respective expectedRequestParameter(), expectedBodyField(), and expectedBodyFile() methods INSIDE of the super constructor of your handler class.

Example Usage

Click to expand

The ExpectedRequestParameter object is used to get the expected parameters of the request. You can use the getter methods to safely get the parameter value, such as getString, getInt, getDouble, and getBoolean methods to safely cast the parameter to the expected type.

java
@RouteInfo(method = HttpMethod.GET, path = "/hello")
+public class MyHandler extends RequestHandler {
+    // Store the expected parameter in a private field
+    private final ExpectedRequestParameter myExpectedReqParam;
+    public MyHandler(Request req, Response res) {
+        super(req, res);
+        // Get the expected parameter "myParam", and optionally provide a description
+        myExpectedReqParam = expectedRequestParameter("myParam", "A description of the parameter");
+    }
+
+    @Override
+    public Object handle() {
+        // OPTIONAL: specify the response status code and type
+        res.status(200);
+        res.type("text/plain");
+        
+        // Safely get the parameter value as a String
+        String myParamValue = myExpectedReqParam.getString();
+        
+        // Return the response to the client
+        return "Hello, " + myParamValue + "!";
+    }
+}

Visiting /hello?myParam=John from your browser, will return Hello, John!.

Click to expand

The ExpectedBodyField object is used to get the expected fields of the request body. You can use the getter methods to safely get the field value, such as getString, getInt, getDouble, and getBoolean methods to safely cast the field to the expected type.

java
@RouteInfo(method = HttpMethod.GET, path = "/helloBody")
+public class MyHandler extends RequestHandler {
+    // Store the expected field in a private field
+    private final ExpectedBodyField myExpectedBodyField;
+    public MyHandler(Request req, Response res) {
+        super(req, res);
+        // Get the expected field "myField", and optionally provide a description
+        myExpectedBodyField = expectedBodyField("myField", "A description of the field");
+    }
+
+    @Override
+    public Object handle() {
+        // OPTIONAL: specify the response status code and type
+        res.status(200);
+        res.type("text/plain");
+        
+        // Safely get the field value as a String
+        String myFieldValue = myExpectedBodyField.getString();
+        
+        // Return the response to the client
+        return "Field value: " + myFieldValue;
+    }
+}

This time, since we are expecting a field in the request body, using our browser would not be enough to test the handler. Instead, you can use a tool like Postman to send a GET request to /helloBody with a multipart form data body containing a field named myField. You should receive a response like Field value: <value>.

Click to expand

The ExpectedBodyFile object is used to get the expected files of the request body. The methods provided by this object are slightly different from the other two, but still extremely powerful and simple to use.

java
@RouteInfo(method = HttpMethod.POST, path = "/helloFile")
+public class MyHandler extends RequestHandler {
+    // Store the expected file in a private field
+    private final ExpectedBodyFile myExpectedBodyFile;
+    public MyHandler(Request req, Response res) {
+        super(req, res);
+        // Get the expected file "myFile", and optionally provide a description
+        myExpectedBodyFile = expectedBodyFile("myFile", "A description of the file");
+    }
+
+    @Override
+    public Object handle() {
+        // OPTIONAL: specify the response status code and type
+        res.status(200);
+        res.type("text/plain");
+        
+        // Write the file to filesystem and get the File object
+        File myFile = myExpectedBodyFile.createFile(Paths.get("path/to/save"));
+        
+        // Return the response to the client
+        return "File saved at: " + myFile.getAbsolutePath();
+    }
+}

This time, you will need to use a tool like Postman to send a POST request to /helloFile with a multipart form data body containing a file named myFile. You should receive a response like File saved at: <path> where <path> is the location where the server saved the file.

`,19)]))}const c=i(n,[["render",h]]);export{E as __pageData,c as default}; diff --git a/assets/flash_request-response.md.AHTHQW77.lean.js b/assets/flash_request-response.md.AHTHQW77.lean.js new file mode 100644 index 0000000..762c32c --- /dev/null +++ b/assets/flash_request-response.md.AHTHQW77.lean.js @@ -0,0 +1,67 @@ +import{_ as i,c as e,a0 as a,o as t}from"./chunks/framework.p2VkXzrt.js";const E=JSON.parse('{"title":"📥 Request and Response","description":"","frontmatter":{"banner_title":"Flash - Request and Response","banner_description":"Unlock the power of the Request and Response objects in Flash.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-request-response.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-request-response.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/request-response.md","filePath":"flash/request-response.md"}'),n={name:"flash/request-response.md"};function h(l,s,p,k,d,r){return t(),e("div",null,s[0]||(s[0]=[a(`

📥 Request and Response

Generally speaking, in an HTTP request-response cycle, the client sends a request to the server, and the server generates a response based on the request. In this section, we illustrate the Request and Response objects in Flash, which are used to interact with the request data and generate responses. We will learn to read and interpret the request data, and model the correct response to send back to the client.

Request and Response Objects

The Request and Response objects are passed to the handler constructor and provide access to the request data and response methods. Under the hood, these objects are provided by the RouteController during the registration stage of the handler, and they are continously updated for each request on that specific handler's instance, but for now, you can consider them as magic.

These objects are a very powerful tool to interact with the lifecycle of the server's response, and they provide a wide range of methods that makes our lives as developers easier.

RequestHandler context

Inside a RequestHandler class, you can access the Request and Response objects inside the handle method by simply typing req and res, respectively. You can use the methods provided by these objects both outside of a handler, and inside of it, to interact with the request and response objects. Although, the advantages of being inside a handler are that Flash supports out-of-the-box methods that can significantly clean up your code and improve the overall readability of your handlers.

Specifically, the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile objects are used to get the expected properties of the request, and they are used by Flash to validate the request data before the handle method is even executed.

The developer can then simply assume that all the expected parameters are present and valid, without having to write a single line of validation code: Flash will do it for you.

The three objects mentioned above are fairly similar in their usage, providing getter methods which safely return the data in the expected format and type, thanks to Flash's built-in validation and casting system.

NOTE

Flash will take care of informing the client of any missing or invalid parameters, parameters that are not in the expected format, or any other kind of error that might occur during the validation process.

REMEMBER

The ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile instances are ONLY supposed to be retrieved by calling the respective expectedRequestParameter(), expectedBodyField(), and expectedBodyFile() methods INSIDE of the super constructor of your handler class.

Example Usage

Click to expand

The ExpectedRequestParameter object is used to get the expected parameters of the request. You can use the getter methods to safely get the parameter value, such as getString, getInt, getDouble, and getBoolean methods to safely cast the parameter to the expected type.

java
@RouteInfo(method = HttpMethod.GET, path = "/hello")
+public class MyHandler extends RequestHandler {
+    // Store the expected parameter in a private field
+    private final ExpectedRequestParameter myExpectedReqParam;
+    public MyHandler(Request req, Response res) {
+        super(req, res);
+        // Get the expected parameter "myParam", and optionally provide a description
+        myExpectedReqParam = expectedRequestParameter("myParam", "A description of the parameter");
+    }
+
+    @Override
+    public Object handle() {
+        // OPTIONAL: specify the response status code and type
+        res.status(200);
+        res.type("text/plain");
+        
+        // Safely get the parameter value as a String
+        String myParamValue = myExpectedReqParam.getString();
+        
+        // Return the response to the client
+        return "Hello, " + myParamValue + "!";
+    }
+}

Visiting /hello?myParam=John from your browser, will return Hello, John!.

Click to expand

The ExpectedBodyField object is used to get the expected fields of the request body. You can use the getter methods to safely get the field value, such as getString, getInt, getDouble, and getBoolean methods to safely cast the field to the expected type.

java
@RouteInfo(method = HttpMethod.GET, path = "/helloBody")
+public class MyHandler extends RequestHandler {
+    // Store the expected field in a private field
+    private final ExpectedBodyField myExpectedBodyField;
+    public MyHandler(Request req, Response res) {
+        super(req, res);
+        // Get the expected field "myField", and optionally provide a description
+        myExpectedBodyField = expectedBodyField("myField", "A description of the field");
+    }
+
+    @Override
+    public Object handle() {
+        // OPTIONAL: specify the response status code and type
+        res.status(200);
+        res.type("text/plain");
+        
+        // Safely get the field value as a String
+        String myFieldValue = myExpectedBodyField.getString();
+        
+        // Return the response to the client
+        return "Field value: " + myFieldValue;
+    }
+}

This time, since we are expecting a field in the request body, using our browser would not be enough to test the handler. Instead, you can use a tool like Postman to send a GET request to /helloBody with a multipart form data body containing a field named myField. You should receive a response like Field value: <value>.

Click to expand

The ExpectedBodyFile object is used to get the expected files of the request body. The methods provided by this object are slightly different from the other two, but still extremely powerful and simple to use.

java
@RouteInfo(method = HttpMethod.POST, path = "/helloFile")
+public class MyHandler extends RequestHandler {
+    // Store the expected file in a private field
+    private final ExpectedBodyFile myExpectedBodyFile;
+    public MyHandler(Request req, Response res) {
+        super(req, res);
+        // Get the expected file "myFile", and optionally provide a description
+        myExpectedBodyFile = expectedBodyFile("myFile", "A description of the file");
+    }
+
+    @Override
+    public Object handle() {
+        // OPTIONAL: specify the response status code and type
+        res.status(200);
+        res.type("text/plain");
+        
+        // Write the file to filesystem and get the File object
+        File myFile = myExpectedBodyFile.createFile(Paths.get("path/to/save"));
+        
+        // Return the response to the client
+        return "File saved at: " + myFile.getAbsolutePath();
+    }
+}

This time, you will need to use a tool like Postman to send a POST request to /helloFile with a multipart form data body containing a file named myFile. You should receive a response like File saved at: <path> where <path> is the location where the server saved the file.

`,19)]))}const c=i(n,[["render",h]]);export{E as __pageData,c as default}; diff --git a/assets/flash_server-router.md.bhWEamQT.js b/assets/flash_server-router.md.bhWEamQT.js new file mode 100644 index 0000000..04fd70c --- /dev/null +++ b/assets/flash_server-router.md.bhWEamQT.js @@ -0,0 +1,27 @@ +import{_ as i,c as a,a0 as e,o as n}from"./chunks/framework.p2VkXzrt.js";const E=JSON.parse('{"title":"🛣️ Server Router","description":"","frontmatter":{"banner_title":"Flash - Server Router","banner_description":"Learn how to use the FlashServer router to create and manage RouteHandlers.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-server-router.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-server-router.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/server-router.md","filePath":"flash/server-router.md"}'),t={name:"flash/server-router.md"};function h(l,s,p,r,k,d){return n(),a("div",null,s[0]||(s[0]=[e(`

🛣️ Server Router

In this section, we discuss how to use the FlashServer router to manage our RequestHandler instances. The router is used to define route endpoints and their corresponding handler, which are executed when a request is made to the server.

The FlashServer router is an instance of the RouteController class, each server instance has its own router instance. To access the router instance, you can call the route() method on the FlashServer instance.

Creating a Route

To create a route, you need to call the route() method on your server's instance (in this case for simplicity, on the InternalFlashServer) and specify the base path of the route, followed by your handler class,

java
// Example.java
+public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+        
+        server.route("/api")
+            .register(MyHandler.class);
+            
+        server.start();
+    }
+}
java
// MyHandler.java
+import flash.Request;
+import flash.Response;
+import flash.models.RequestHandler;
+
+@RouteInfo(method = HttpMethod.GET, path = "/hello")
+public class MyHandler extends RequestHandler {
+    public MyHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        String response = "Hello, world!";
+        return response;
+    }
+}

In the example above, we create a route /api and register the MyHandler class to handle requests on the /api/hello endpoint.

This is because the path property of the RouteInfo annotation is relative to the base path of the route, which in this case is /api.

Visiting /api/hello from your browser will result in the response Hello, world!.

`,10)]))}const c=i(t,[["render",h]]);export{E as __pageData,c as default}; diff --git a/assets/flash_server-router.md.bhWEamQT.lean.js b/assets/flash_server-router.md.bhWEamQT.lean.js new file mode 100644 index 0000000..04fd70c --- /dev/null +++ b/assets/flash_server-router.md.bhWEamQT.lean.js @@ -0,0 +1,27 @@ +import{_ as i,c as a,a0 as e,o as n}from"./chunks/framework.p2VkXzrt.js";const E=JSON.parse('{"title":"🛣️ Server Router","description":"","frontmatter":{"banner_title":"Flash - Server Router","banner_description":"Learn how to use the FlashServer router to create and manage RouteHandlers.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-server-router.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-server-router.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/server-router.md","filePath":"flash/server-router.md"}'),t={name:"flash/server-router.md"};function h(l,s,p,r,k,d){return n(),a("div",null,s[0]||(s[0]=[e(`

🛣️ Server Router

In this section, we discuss how to use the FlashServer router to manage our RequestHandler instances. The router is used to define route endpoints and their corresponding handler, which are executed when a request is made to the server.

The FlashServer router is an instance of the RouteController class, each server instance has its own router instance. To access the router instance, you can call the route() method on the FlashServer instance.

Creating a Route

To create a route, you need to call the route() method on your server's instance (in this case for simplicity, on the InternalFlashServer) and specify the base path of the route, followed by your handler class,

java
// Example.java
+public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+        
+        server.route("/api")
+            .register(MyHandler.class);
+            
+        server.start();
+    }
+}
java
// MyHandler.java
+import flash.Request;
+import flash.Response;
+import flash.models.RequestHandler;
+
+@RouteInfo(method = HttpMethod.GET, path = "/hello")
+public class MyHandler extends RequestHandler {
+    public MyHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        String response = "Hello, world!";
+        return response;
+    }
+}

In the example above, we create a route /api and register the MyHandler class to handle requests on the /api/hello endpoint.

This is because the path property of the RouteInfo annotation is relative to the base path of the route, which in this case is /api.

Visiting /api/hello from your browser will result in the response Hello, world!.

`,10)]))}const c=i(t,[["render",h]]);export{E as __pageData,c as default}; diff --git a/assets/flash_websockets.md.DzzW1sdE.js b/assets/flash_websockets.md.DzzW1sdE.js new file mode 100644 index 0000000..7f78ed5 --- /dev/null +++ b/assets/flash_websockets.md.DzzW1sdE.js @@ -0,0 +1,31 @@ +import{_ as i,c as a,a0 as e,o as n}from"./chunks/framework.p2VkXzrt.js";const E=JSON.parse('{"title":"🌐 Websockets","description":"","frontmatter":{"banner_title":"Flash - Websockets","banner_description":"Learn how to create and manage server-side Websockets in Flash.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-websockets.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-websockets.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/websockets.md","filePath":"flash/websockets.md"}'),t={name:"flash/websockets.md"};function h(l,s,k,p,r,o){return n(),a("div",null,s[0]||(s[0]=[e(`

🌐 Websockets

In this section, we illustrate how to create and manage Websockets in Flash, which are used to establish a bidirectional communication channel between the client and the server.

Understanding Websockets

Websockets are a communication protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP, which is a request-response protocol, Websockets allow for real-time, low-latency communication between the client and the server.

Imagine a scenario where you need to send real-time updates to the client, such as a chat application or a live feed: if you were to use HTTP, you would need to poll the server at regular intervals to check for updates, which is inefficient and resource-intensive.

Creating a Websocket

To create a Websocket in Flash, you need to extend the WebsocketHandler class and override the onOpen, onMessage, onClose, and onError methods.

java
public class MyWebsocketHandler extends WebsocketHandler {
+    @Override
+    public void onOpen(WebSocketSession session) {
+        System.out.println("WebSocket connection opened");
+    }
+
+    @Override
+    public void onClose(WebSocketSession session, int statusCode, String reason) {
+        System.out.println("WebSocket connection closed");
+    }
+
+    @Override
+    public void onMessage(WebSocketSession session, String message) {
+        System.out.println("Received message: " + message);
+        session.sendMessage("I received your message!");
+    }
+
+    @Override
+    public void onError(WebSocketSession session, Throwable error) {
+        System.out.println("WebSocket error: " + error.getMessage());
+    }
+}

To register your Websocket handler with the server, you can use the server.ws() method:

java
public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+
+        server.ws("/ws")
+            .register(MyWebsocketHandler.class);
+
+        server.start();
+    }
+}

Interacting with Websockets sessions

The WebSocketSession object provides methods to interact with the Websocket session, such as sending messages, closing the connection, and getting the remote address and session ID.

`,12)]))}const c=i(t,[["render",h]]);export{E as __pageData,c as default}; diff --git a/assets/flash_websockets.md.DzzW1sdE.lean.js b/assets/flash_websockets.md.DzzW1sdE.lean.js new file mode 100644 index 0000000..7f78ed5 --- /dev/null +++ b/assets/flash_websockets.md.DzzW1sdE.lean.js @@ -0,0 +1,31 @@ +import{_ as i,c as a,a0 as e,o as n}from"./chunks/framework.p2VkXzrt.js";const E=JSON.parse('{"title":"🌐 Websockets","description":"","frontmatter":{"banner_title":"Flash - Websockets","banner_description":"Learn how to create and manage server-side Websockets in Flash.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-websockets.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-websockets.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/websockets.md","filePath":"flash/websockets.md"}'),t={name:"flash/websockets.md"};function h(l,s,k,p,r,o){return n(),a("div",null,s[0]||(s[0]=[e(`

🌐 Websockets

In this section, we illustrate how to create and manage Websockets in Flash, which are used to establish a bidirectional communication channel between the client and the server.

Understanding Websockets

Websockets are a communication protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP, which is a request-response protocol, Websockets allow for real-time, low-latency communication between the client and the server.

Imagine a scenario where you need to send real-time updates to the client, such as a chat application or a live feed: if you were to use HTTP, you would need to poll the server at regular intervals to check for updates, which is inefficient and resource-intensive.

Creating a Websocket

To create a Websocket in Flash, you need to extend the WebsocketHandler class and override the onOpen, onMessage, onClose, and onError methods.

java
public class MyWebsocketHandler extends WebsocketHandler {
+    @Override
+    public void onOpen(WebSocketSession session) {
+        System.out.println("WebSocket connection opened");
+    }
+
+    @Override
+    public void onClose(WebSocketSession session, int statusCode, String reason) {
+        System.out.println("WebSocket connection closed");
+    }
+
+    @Override
+    public void onMessage(WebSocketSession session, String message) {
+        System.out.println("Received message: " + message);
+        session.sendMessage("I received your message!");
+    }
+
+    @Override
+    public void onError(WebSocketSession session, Throwable error) {
+        System.out.println("WebSocket error: " + error.getMessage());
+    }
+}

To register your Websocket handler with the server, you can use the server.ws() method:

java
public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+
+        server.ws("/ws")
+            .register(MyWebsocketHandler.class);
+
+        server.start();
+    }
+}

Interacting with Websockets sessions

The WebSocketSession object provides methods to interact with the Websocket session, such as sending messages, closing the connection, and getting the remote address and session ID.

`,12)]))}const c=i(t,[["render",h]]);export{E as __pageData,c as default}; diff --git a/assets/index.md.CgmTRI0Q.js b/assets/index.md.CgmTRI0Q.js new file mode 100644 index 0000000..f31535d --- /dev/null +++ b/assets/index.md.CgmTRI0Q.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i}from"./chunks/framework.p2VkXzrt.js";const p=JSON.parse('{"title":"","description":"","frontmatter":{"banner_title":"Pixel Services Documentation","banner_description":"Explore the documentation for all public Pixel Services projects","layout":"home","hero":{"name":"Pixel Services Documentation","text":"","tagline":"Explore the documentation for all public Pixel Services projects","actions":[{"theme":"brand","text":"Support Us","link":"https://buymeacoffee.com/pixelservices"},{"theme":"alt","text":"Check out our Github","link":"https://github.com/Pixel-Services"}]},"features":[{"title":"Flash","details":"Simple, modern and fast expressive web framework written in Java.","link":"/flash"},{"title":"ServerLibraries","details":"A Library made to ease server-side Fabric mod development.","link":"/serverlibraries"},{"title":"MoBot","details":"A Modular Discord Bot with a simple but efficent API.","link":"/mobot"}],"head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/index.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/index.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"index.md","filePath":"index.md"}'),a={name:"index.md"};function n(r,o,s,c,m,l){return i(),t("div")}const h=e(a,[["render",n]]);export{p as __pageData,h as default}; diff --git a/assets/index.md.CgmTRI0Q.lean.js b/assets/index.md.CgmTRI0Q.lean.js new file mode 100644 index 0000000..f31535d --- /dev/null +++ b/assets/index.md.CgmTRI0Q.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as i}from"./chunks/framework.p2VkXzrt.js";const p=JSON.parse('{"title":"","description":"","frontmatter":{"banner_title":"Pixel Services Documentation","banner_description":"Explore the documentation for all public Pixel Services projects","layout":"home","hero":{"name":"Pixel Services Documentation","text":"","tagline":"Explore the documentation for all public Pixel Services projects","actions":[{"theme":"brand","text":"Support Us","link":"https://buymeacoffee.com/pixelservices"},{"theme":"alt","text":"Check out our Github","link":"https://github.com/Pixel-Services"}]},"features":[{"title":"Flash","details":"Simple, modern and fast expressive web framework written in Java.","link":"/flash"},{"title":"ServerLibraries","details":"A Library made to ease server-side Fabric mod development.","link":"/serverlibraries"},{"title":"MoBot","details":"A Modular Discord Bot with a simple but efficent API.","link":"/mobot"}],"head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/index.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/index.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"index.md","filePath":"index.md"}'),a={name:"index.md"};function n(r,o,s,c,m,l){return i(),t("div")}const h=e(a,[["render",n]]);export{p as __pageData,h as default}; diff --git a/assets/mobot_index.md.Be0Zoetq.js b/assets/mobot_index.md.Be0Zoetq.js new file mode 100644 index 0000000..d598113 --- /dev/null +++ b/assets/mobot_index.md.Be0Zoetq.js @@ -0,0 +1 @@ +import{_ as t,c as e,o as n}from"./chunks/framework.p2VkXzrt.js";const _=JSON.parse('{"title":"","description":"","frontmatter":{"banner_title":"MoBot - A Modular Discord Bot","banner_description":"MoBot is a modular Discord bot written in Java, featuring a beginner-friendly api and extensive documentation.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/mobot-index.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/mobot-index.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"mobot/index.md","filePath":"mobot/index.md"}'),a={name:"mobot/index.md"};function o(i,r,m,s,c,d){return n(),e("div")}const l=t(a,[["render",o]]);export{_ as __pageData,l as default}; diff --git a/assets/mobot_index.md.Be0Zoetq.lean.js b/assets/mobot_index.md.Be0Zoetq.lean.js new file mode 100644 index 0000000..d598113 --- /dev/null +++ b/assets/mobot_index.md.Be0Zoetq.lean.js @@ -0,0 +1 @@ +import{_ as t,c as e,o as n}from"./chunks/framework.p2VkXzrt.js";const _=JSON.parse('{"title":"","description":"","frontmatter":{"banner_title":"MoBot - A Modular Discord Bot","banner_description":"MoBot is a modular Discord bot written in Java, featuring a beginner-friendly api and extensive documentation.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/mobot-index.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/mobot-index.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"mobot/index.md","filePath":"mobot/index.md"}'),a={name:"mobot/index.md"};function o(i,r,m,s,c,d){return n(),e("div")}const l=t(a,[["render",o]]);export{_ as __pageData,l as default}; diff --git a/assets/serverlibraries_index.md.CeIqSPIs.js b/assets/serverlibraries_index.md.CeIqSPIs.js new file mode 100644 index 0000000..1f44c81 --- /dev/null +++ b/assets/serverlibraries_index.md.CeIqSPIs.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as r}from"./chunks/framework.p2VkXzrt.js";const l=JSON.parse('{"title":"","description":"","frontmatter":{"banner_title":"ServerLibraries - Fabric mod library","banner_description":"ServerLibraries provides a simple API for creating server-side mods, with tons of Bukkit-like features.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/serverlibraries-index.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/serverlibraries-index.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"serverlibraries/index.md","filePath":"serverlibraries/index.md"}'),a={name:"serverlibraries/index.md"};function i(n,s,o,c,m,d){return r(),t("div")}const _=e(a,[["render",i]]);export{l as __pageData,_ as default}; diff --git a/assets/serverlibraries_index.md.CeIqSPIs.lean.js b/assets/serverlibraries_index.md.CeIqSPIs.lean.js new file mode 100644 index 0000000..1f44c81 --- /dev/null +++ b/assets/serverlibraries_index.md.CeIqSPIs.lean.js @@ -0,0 +1 @@ +import{_ as e,c as t,o as r}from"./chunks/framework.p2VkXzrt.js";const l=JSON.parse('{"title":"","description":"","frontmatter":{"banner_title":"ServerLibraries - Fabric mod library","banner_description":"ServerLibraries provides a simple API for creating server-side mods, with tons of Bukkit-like features.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/serverlibraries-index.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/serverlibraries-index.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"serverlibraries/index.md","filePath":"serverlibraries/index.md"}'),a={name:"serverlibraries/index.md"};function i(n,s,o,c,m,d){return r(),t("div")}const _=e(a,[["render",i]]);export{l as __pageData,_ as default}; diff --git a/flash/handler-default-implementation.html b/flash/handler-default-implementation.html new file mode 100644 index 0000000..4d0433c --- /dev/null +++ b/flash/handler-default-implementation.html @@ -0,0 +1,126 @@ + + + + + + ⚡ Handler Default Implementations (HDI) | Pixel Services Docs + + + + + + + + + + + + + + + + + + + + + +
Skip to content

⚡ Handler Default Implementations (HDI)

HDI's provide an elegant and reliable way to standardize common behaviors across multiple request handlers. By defining base handlers that extend RequestHandler (or even chaining multiple base handlers), you can modularize your logic for aspects like authentication, user data retrieval, and rate limiting.

🔗 How It Works

Instead of implementing repeated logic in every handler, you create abstract handler classes that encapsulate common functionality. Your actual route handlers then extend these base classes, inheriting their behavior while focusing purely on request-specific logic.

🛠 Example: API Key Authentication

Imagine you want to protect API endpoints with an authentication key and track API usage in a database. You can create an abstract base handler that enforces API key validation:

java
public abstract class APIKeyProtectedHandler extends RequestHandler {
+    protected String apiKey;
+    protected int remainingQuota;
+
+    public APIKeyProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        apiKey = req.header("X-API-Key");
+
+        if (apiKey == null || !isValidApiKey(apiKey)) {
+            res.status(403);
+            return "{\"error\":\"Invalid API Key\"}";
+        }
+
+        remainingQuota = getRemainingQuota(apiKey);
+        if (remainingQuota <= 0) {
+            res.status(429);
+            return "{\"error\":\"API quota exceeded\"}";
+        }
+
+        return handleAuthorized();
+    }
+
+    protected abstract Object handleAuthorized();
+
+    private boolean isValidApiKey(String key) {
+        // Implement key validation logic, e.g., checking against a database
+        return true;
+    }
+
+    private int getRemainingQuota(String key) {
+        // Retrieve remaining quota from a database
+        return 100;
+    }
+}

Now, your actual API handlers only need to extend APIKeyProtectedHandler, ensuring every request has a valid API key and available quota before executing its logic:

java
@RouteInfo(method = HttpMethod.GET, path = "/data")
+public class GetDataHandler extends APIKeyProtectedHandler {
+    public GetDataHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthorized() {
+        return "{\"data\":\"Your API response here\"}";
+    }
+}

🏗️ Chaining HDIs for Modular Logic

HDIs can be chained together to compose multiple layers of behavior. For example:

  • ProtectedHandler ensures authentication.
java
public abstract class ProtectedHandler extends RequestHandler {
+protected String authToken;
+
+    public ProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        authToken = req.header("Authorization");
+        if (authToken == null || !isValidToken(authToken)) {
+            res.status(401);
+            res.type("application/json");
+            return "{\"error\":\"Unauthorized\"}";
+        }
+        return handleAuthenticated();
+    }
+
+    protected abstract Object handleAuthenticated();
+}
  • AuthenticatedHandler extends ProtectedHandler to fetch user data from the database.
java
public abstract class AuthenticatedHandler extends ProtectedHandler {
+    protected User user;
+
+    public AuthenticatedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthenticated() {
+        user = getUserFromDatabase(authToken);
+        if (user == null) {
+            res.status(403);
+            return "{\"error\":\"User not found\"}";
+        }
+        return handleWithUser();
+    }
+
+    protected abstract Object handleWithUser();
+}
  • Your actual implementation handler extends AuthenticatedHandler to work directly with user data.
java
@RouteInfo(method = HttpMethod.GET, path = "/profile")
+public class UserProfileHandler extends AuthenticatedHandler {
+    public UserProfileHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleWithUser() {
+        return "{\"username\":\"" + user.getUsername() + "\"}";
+    }
+}
+ + + + \ No newline at end of file diff --git a/flash/index.html b/flash/index.html index 5c28519..b3d8473 100644 --- a/flash/index.html +++ b/flash/index.html @@ -9,11 +9,11 @@ - + - - - + + + @@ -25,8 +25,8 @@ -
Skip to content

⚡ Flash

Flash is a simple, modern and fast expressive web framework written in Java. The project is maintained by Pixel Services and the open-source community.

Our Team

Say hello to the Pixel Services team !

Relism

Relism

Developer

Sieadev

Sieadev

Developer

- +
Skip to content

⚡ Flash

Flash is a simple, modern and fast expressive web framework written in Java. The project is maintained by Pixel Services and the open-source community.

Our Team

Say hello to the Pixel Services team !

Relism

Relism

Developer

Sieadev

Sieadev

Developer

+ \ No newline at end of file diff --git a/flash/installation.html b/flash/installation.html index d147f0f..446faa4 100644 --- a/flash/installation.html +++ b/flash/installation.html @@ -9,11 +9,11 @@ - + - - - + + + @@ -25,7 +25,7 @@ -
Skip to content

📲 Installation

This page provides installation instructions for the latest version of the flash library from Pixel Services.

Installation

Maven (pom.xml)

  1. Add the repository :

    xml
    <repositories>
    +    
    Skip to content

    📲 Installation

    This page provides installation instructions for the latest version of the flash library from Pixel Services.

    Installation

    Maven (pom.xml)

    1. Add the repository :

      xml
      <repositories>
         <repository>
           <id>pixel-services</id>
           <name>Pixel Services</name>
      @@ -43,8 +43,8 @@
           }
       }
    2. And the dependency :

      groovy
      dependencies {
           implementation 'com.pixelservices:flash:{{ latestVersion }}'
      -}
    ⚡ Latest version:
    - +}
⚡ Latest version:
+ \ No newline at end of file diff --git a/flash/request-handler.html b/flash/request-handler.html index fe84147..6446f70 100644 --- a/flash/request-handler.html +++ b/flash/request-handler.html @@ -9,11 +9,11 @@ - + - - - + + + @@ -25,11 +25,7 @@ -
Skip to content

⚙️ Request Handler

In this section, we illustrate the powerful concept of RequestHandler in Flash, which are used to handle incoming requests and generate responses.

Creating a Request Handler

To create a custom request handler, you need to extend the RequestHandler class and annotate the class with the RouteInfo annotation, specifying the HTTP method that the handler will respond to and the relative path that the handler will be registered to. The enforceNonNullBody attribute is used to specify whether the handler expects a non-null request body: by default, it is set to false. After that, you must override the handle method; The handle method is where you define the logic for processing the request and generating the response. The req (request) and res (response) objects are available in the handler to access the request data and send the response back to the client.

You must call the super constructor with the req and res objects to initialize the handler.

java
import flash.Request;
-import flash.Response;
-import flash.RequestHandler;
-
-@RouteInfo(method = HttpMethod.GET, path = "/hello", enforceNonNullBody = false)
+    
Skip to content

⚙️ Request Handler

In this section, we illustrate the powerful concept of RequestHandler in Flash, which are used to handle incoming requests and generate responses.

Creating a Request Handler

To create a custom request handler, you need to extend the RequestHandler class and annotate the class with the RouteInfo annotation, specifying the HTTP method that the handler will respond to and the relative path that the handler will be registered to. After that, you must override the handle method; The handle method is where you define the logic for processing the request and generating the response. The req (request) and res (response) objects are available in the handler to access the request data and send the response back to the client.

You must call the super constructor with the req and res objects to initialize the handler.

java
@RouteInfo(method = HttpMethod.GET, path = "/hello")
 public class MyHandler extends RequestHandler {
     public MyHandler(Request req, Response res) {
         super(req, res);
@@ -40,8 +36,8 @@
         String response = "Hello, world!";
         return response;
     }
-}

WARNING

Any logic that needs to be executed before the request handler is registered must be done within the constructor.

Request Handler methods

The RequestHandler class provides several methods that can be used to interact with the request and response objects easily and safely. Following are listed the methods available in the RequestHandler class, with a brief description of their purpose:

MethodParamsDescription
getRequestBody()noneReturns a JSONObject representation of the request body.
getSpecification()noneReturns an instance of HandlerSpecification containing all sorts of information about the handler.
expectedRequestParameter()String name, descriptionReturns an instance of ExpectedRequestParameter for the specified parameter name.
expectedBodyField()String name, descriptionReturns an instance of ExpectedBodyField for the specified field name.
expectedBodyFile()String name, descriptionReturns an instance of ExpectedBodyFile for the specified file name.

(More on the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile classes in the next section).

- +}

WARNING

Any logic that needs to be executed before the request handler is registered must be done within the constructor.

Request Handler methods

The RequestHandler class provides several methods that can be used to interact with the request and response objects easily and safely. Following are listed the methods available in the RequestHandler class, with a brief description of their purpose:

MethodParamsDescription
getRequestBody()noneReturns a JSONObject representation of the request body.
getSpecification()noneReturns an instance of HandlerSpecification containing all sorts of information about the handler.
expectedRequestParameter()String name, descriptionReturns an instance of ExpectedRequestParameter for the specified parameter name.
expectedBodyField()String name, descriptionReturns an instance of ExpectedBodyField for the specified field name.
expectedBodyFile()String name, descriptionReturns an instance of ExpectedBodyFile for the specified file name.

(More on the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile classes in the next section).

+ \ No newline at end of file diff --git a/flash/request-response.html b/flash/request-response.html index 6bab87e..82a77b8 100644 --- a/flash/request-response.html +++ b/flash/request-response.html @@ -9,11 +9,11 @@ - + - - - + + + @@ -25,49 +25,38 @@ -
Skip to content

📥 Request and Response

Generally speaking, in an HTTP request-response cycle, the client sends a request to the server, and the server generates a response based on the request. In this section, we illustrate the Request and Response objects in Flash, which are used to interact with the request data and generate responses. We will learn to read and interpret the request data, and model the correct response to send back to the client.

Request and Response Objects

The Request and Response objects are passed to the handler constructor and provide access to the request data and response methods. Under the hood, these objects are provided by the RouteController during the registration stage of the handler, and they are continously updated for each request on that specific handler's instance, but for now, you can consider them as magic.

These objects are a very powerful tool to interact with the lifecycle of the server's response, and they provide a wide range of methods that makes our lives as developers easier.

RequestHandler context

Inside a RequestHandler class, you can access the Request and Response objects inside the handle method by simply typing req and res, respectively. You can use the methods provided by these objects both outside of a handler, and inside of it, to interact with the request and response objects. Although, the advantages of being inside a handler are that Flash supports out-of-the-box methods that can significantly clean up your code and improve the overall readability of your handlers.

Specifically, the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile objects are used to get the expected properties of the request, and they are used by Flash to validate the request data before the handle method is even executed.

The developer can then simply assume that all the expected parameters are present and valid, without having to write a single line of validation code: Flash will do it for you.

The three objects mentioned above are fairly similar in their usage, providing getter methods which safely return the data in the expected format and type, thanks to Flash's built-in validation and casting system.

NOTE

Flash will take care of informing the client of any missing or invalid parameters, parameters that are not in the expected format, or any other kind of error that might occur during the validation process.

REMEMBER

The ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile instances are ONLY supposed to be retrieved by calling the respective expectedRequestParameter(), expectedBodyField(), and expectedBodyFile() methods INSIDE of the super constructor of your handler class.

Example Usage

  • ExpectedRequestParameter
Click to expand

The ExpectedRequestParameter object is used to get the expected parameters of the request. You can use the getter methods to safely get the parameter value, such as getString, getInt, getDouble, and getBoolean methods to safely cast the parameter to the expected type.

java
import flash.Request;
-import flash.Response;
-import flash.models.RequestHandler;
-import flash.models.ExpectedRequestParameter;
-
-@RouteInfo(method = HttpMethod.GET, path = "/hello", enforceNonNullBody = false)
+    
Skip to content

📥 Request and Response

Generally speaking, in an HTTP request-response cycle, the client sends a request to the server, and the server generates a response based on the request. In this section, we illustrate the Request and Response objects in Flash, which are used to interact with the request data and generate responses. We will learn to read and interpret the request data, and model the correct response to send back to the client.

Request and Response Objects

The Request and Response objects are passed to the handler constructor and provide access to the request data and response methods. Under the hood, these objects are provided by the RouteController during the registration stage of the handler, and they are continously updated for each request on that specific handler's instance, but for now, you can consider them as magic.

These objects are a very powerful tool to interact with the lifecycle of the server's response, and they provide a wide range of methods that makes our lives as developers easier.

RequestHandler context

Inside a RequestHandler class, you can access the Request and Response objects inside the handle method by simply typing req and res, respectively. You can use the methods provided by these objects both outside of a handler, and inside of it, to interact with the request and response objects. Although, the advantages of being inside a handler are that Flash supports out-of-the-box methods that can significantly clean up your code and improve the overall readability of your handlers.

Specifically, the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile objects are used to get the expected properties of the request, and they are used by Flash to validate the request data before the handle method is even executed.

The developer can then simply assume that all the expected parameters are present and valid, without having to write a single line of validation code: Flash will do it for you.

The three objects mentioned above are fairly similar in their usage, providing getter methods which safely return the data in the expected format and type, thanks to Flash's built-in validation and casting system.

NOTE

Flash will take care of informing the client of any missing or invalid parameters, parameters that are not in the expected format, or any other kind of error that might occur during the validation process.

REMEMBER

The ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile instances are ONLY supposed to be retrieved by calling the respective expectedRequestParameter(), expectedBodyField(), and expectedBodyFile() methods INSIDE of the super constructor of your handler class.

Example Usage

  • ExpectedRequestParameter
Click to expand

The ExpectedRequestParameter object is used to get the expected parameters of the request. You can use the getter methods to safely get the parameter value, such as getString, getInt, getDouble, and getBoolean methods to safely cast the parameter to the expected type.

java
@RouteInfo(method = HttpMethod.GET, path = "/hello")
 public class MyHandler extends RequestHandler {
-    // Store the expected parameter in a private field
-    private final ExpectedRequestParameter myExpectedReqParam;
+    // Store the expected parameter in a private field
+    private final ExpectedRequestParameter myExpectedReqParam;
     public MyHandler(Request req, Response res) {
-        super(req, res);
+        super(req, res);
         // Get the expected parameter "myParam", and optionally provide a description
-        myExpectedReqParam = expectedRequestParameter("myParam", "A description of the parameter");
+        myExpectedReqParam = expectedRequestParameter("myParam", "A description of the parameter");
     }
 
-    @Override
+    @Override
     public Object handle() {
         // OPTIONAL: specify the response status code and type
         res.status(200);
         res.type("text/plain");
         
         // Safely get the parameter value as a String
-        String myParamValue = myExpectedReqParam.getString();
+        String myParamValue = myExpectedReqParam.getString();
         
         // Return the response to the client
-        return "Hello, " + myParamValue + "!";
+        return "Hello, " + myParamValue + "!";
     }
-}

Visiting /hello?myParam=John from your browser, will return Hello, John!.

  • ExpectedBodyField
Click to expand

The ExpectedBodyField object is used to get the expected fields of the request body. You can use the getter methods to safely get the field value, such as getString, getInt, getDouble, and getBoolean methods to safely cast the field to the expected type.

java
import flash.Request;
-import flash.Response;
-import flash.models.RequestHandler;
-import flash.models.ExpectedBodyField;
-
-// Make sure to set enforceNonNullBody to true
-@RouteInfo(method = HttpMethod.GET, path = "/helloBody", enforceNonNullBody = true)
-public class MyHandler extends RequestHandler {
+}

Visiting /hello?myParam=John from your browser, will return Hello, John!.

  • ExpectedBodyField
Click to expand

The ExpectedBodyField object is used to get the expected fields of the request body. You can use the getter methods to safely get the field value, such as getString, getInt, getDouble, and getBoolean methods to safely cast the field to the expected type.

java
@RouteInfo(method = HttpMethod.GET, path = "/helloBody")
+public class MyHandler extends RequestHandler {
     // Store the expected field in a private field
-    private final ExpectedBodyField myExpectedBodyField;
-    public MyHandler(Request req, Response res) {
+    private final ExpectedBodyField myExpectedBodyField;
+    public MyHandler(Request req, Response res) {
         super(req, res);
         // Get the expected field "myField", and optionally provide a description
-        myExpectedBodyField = expectedBodyField("myField", "A description of the field");
+        myExpectedBodyField = expectedBodyField("myField", "A description of the field");
     }
-
+
     @Override
     public Object handle() {
         // OPTIONAL: specify the response status code and type
@@ -75,27 +64,21 @@
         res.type("text/plain");
         
         // Safely get the field value as a String
-        String myFieldValue = myExpectedBodyField.getString();
+        String myFieldValue = myExpectedBodyField.getString();
         
         // Return the response to the client
-        return "Field value: " + myFieldValue;
+        return "Field value: " + myFieldValue;
     }
-}

This time, since we are expecting a field in the request body, using our browser would not be enough to test the handler. Instead, you can use a tool like Postman to send a GET request to /helloBody with a multipart form data body containing a field named myField. You should receive a response like Field value: <value>.

  • ExpectedBodyFile
Click to expand

The ExpectedBodyFile object is used to get the expected files of the request body. The methods provided by this object are slightly different from the other two, but still extremely powerful and simple to use.

  • createFile(Path/String) accepts either a Path or a String as input. It writes the file's contents to the specified location on the filesystem and returns a File object for further interaction.
  • getFileName() simply returns the name of the file specified by the client.
  • getInputStream() returns an InputStream object containing the file's contents.
java
import flash.Request;
-import flash.Response;
-import flash.models.RequestHandler;
-import flash.models.ExpectedBodyFile;
-
-// Make sure to set enforceNonNullBody to true
-@RouteInfo(method = HttpMethod.POST, path = "/helloFile", enforceNonNullBody = true)
-public class MyHandler extends RequestHandler {
+}

This time, since we are expecting a field in the request body, using our browser would not be enough to test the handler. Instead, you can use a tool like Postman to send a GET request to /helloBody with a multipart form data body containing a field named myField. You should receive a response like Field value: <value>.

  • ExpectedBodyFile
Click to expand

The ExpectedBodyFile object is used to get the expected files of the request body. The methods provided by this object are slightly different from the other two, but still extremely powerful and simple to use.

  • createFile(Path/String) accepts either a Path or a String as input. It writes the file's contents to the specified location on the filesystem and returns a File object for further interaction.
  • getFileName() simply returns the name of the file specified by the client.
  • getInputStream() returns an InputStream object containing the file's contents.
java
@RouteInfo(method = HttpMethod.POST, path = "/helloFile")
+public class MyHandler extends RequestHandler {
     // Store the expected file in a private field
-    private final ExpectedBodyFile myExpectedBodyFile;
-    public MyHandler(Request req, Response res) {
+    private final ExpectedBodyFile myExpectedBodyFile;
+    public MyHandler(Request req, Response res) {
         super(req, res);
         // Get the expected file "myFile", and optionally provide a description
-        myExpectedBodyFile = expectedBodyFile("myFile", "A description of the file");
+        myExpectedBodyFile = expectedBodyFile("myFile", "A description of the file");
     }
-
+
     @Override
     public Object handle() {
         // OPTIONAL: specify the response status code and type
@@ -103,13 +86,13 @@
         res.type("text/plain");
         
         // Write the file to filesystem and get the File object
-        File myFile = myExpectedBodyFile.createFile(Paths.get("path/to/save"));
+        File myFile = myExpectedBodyFile.createFile(Paths.get("path/to/save"));
         
         // Return the response to the client
-        return "File saved at: " + myFile.getAbsolutePath();
+        return "File saved at: " + myFile.getAbsolutePath();
     }
 }

This time, you will need to use a tool like Postman to send a POST request to /helloFile with a multipart form data body containing a file named myFile. You should receive a response like File saved at: <path> where <path> is the location where the server saved the file.

- + \ No newline at end of file diff --git a/flash/server-router.html b/flash/server-router.html index e784d09..6fddc28 100644 --- a/flash/server-router.html +++ b/flash/server-router.html @@ -9,11 +9,11 @@ - + - - - + + + @@ -25,24 +25,22 @@ -
Skip to content

🛣️ Server Router

In this section, we discuss how to use the FlashServer router to manage our RequestHandler instances. The router is used to define route endpoints and their corresponding handler, which are executed when a request is made to the server.

The FlashServer router is an instance of the RouteController class, each server instance has its own router instance. To access the router instance, you can call the route() method on either the internal server or your FlashServer instance.

Creating a Route

To create a route, you need to call the route() method on your server's instance (in this case for simplicity, on the InternalFlashServer) and specify the base path of the route, followed by your handler class and optionally a RouteInterceptor instance.

(More on the concept of RouteInterceptor in the following sections).

java
// Example.java
-import static flash.InternalFlashServer.*;
-
+    
Skip to content

🛣️ Server Router

In this section, we discuss how to use the FlashServer router to manage our RequestHandler instances. The router is used to define route endpoints and their corresponding handler, which are executed when a request is made to the server.

The FlashServer router is an instance of the RouteController class, each server instance has its own router instance. To access the router instance, you can call the route() method on the FlashServer instance.

Creating a Route

To create a route, you need to call the route() method on your server's instance (in this case for simplicity, on the InternalFlashServer) and specify the base path of the route, followed by your handler class,

java
// Example.java
 public class Example {
     public static void main(String[] args) {
-        port(8080);
+        FlashServer server = new FlashServer(8080);
         
-        route("/api")
-            .register(MyHandler.class);
+        server.route("/api")
+            .register(MyHandler.class);
             
-        start();
+        server.start();
     }
 }
java
// MyHandler.java
 import flash.Request;
 import flash.Response;
 import flash.models.RequestHandler;
 
-@RouteInfo(method = HttpMethod.GET, path = "/hello", enforceNonNullBody = false)
+@RouteInfo(method = HttpMethod.GET, path = "/hello")
 public class MyHandler extends RequestHandler {
     public MyHandler(Request req, Response res) {
         super(req, res);
@@ -53,8 +51,8 @@
         String response = "Hello, world!";
         return response;
     }
-}

In the example above, we create a route /api and register the MyHandler class to handle requests on the /api/hello endpoint.

This is because the path property of the RouteInfo annotation is relative to the base path of the route, which in this case is /api.

Visiting /api/hello from your browser will result in the response Hello, world!.

- +}

In the example above, we create a route /api and register the MyHandler class to handle requests on the /api/hello endpoint.

This is because the path property of the RouteInfo annotation is relative to the base path of the route, which in this case is /api.

Visiting /api/hello from your browser will result in the response Hello, world!.

+ \ No newline at end of file diff --git a/flash/websockets.html b/flash/websockets.html new file mode 100644 index 0000000..3e8ef85 --- /dev/null +++ b/flash/websockets.html @@ -0,0 +1,62 @@ + + + + + + 🌐 Websockets | Pixel Services Docs + + + + + + + + + + + + + + + + + + + + + +
Skip to content

🌐 Websockets

In this section, we illustrate how to create and manage Websockets in Flash, which are used to establish a bidirectional communication channel between the client and the server.

Understanding Websockets

Websockets are a communication protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP, which is a request-response protocol, Websockets allow for real-time, low-latency communication between the client and the server.

Imagine a scenario where you need to send real-time updates to the client, such as a chat application or a live feed: if you were to use HTTP, you would need to poll the server at regular intervals to check for updates, which is inefficient and resource-intensive.

Creating a Websocket

To create a Websocket in Flash, you need to extend the WebsocketHandler class and override the onOpen, onMessage, onClose, and onError methods.

java
public class MyWebsocketHandler extends WebsocketHandler {
+    @Override
+    public void onOpen(WebSocketSession session) {
+        System.out.println("WebSocket connection opened");
+    }
+
+    @Override
+    public void onClose(WebSocketSession session, int statusCode, String reason) {
+        System.out.println("WebSocket connection closed");
+    }
+
+    @Override
+    public void onMessage(WebSocketSession session, String message) {
+        System.out.println("Received message: " + message);
+        session.sendMessage("I received your message!");
+    }
+
+    @Override
+    public void onError(WebSocketSession session, Throwable error) {
+        System.out.println("WebSocket error: " + error.getMessage());
+    }
+}

To register your Websocket handler with the server, you can use the server.ws() method:

java
public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+
+        server.ws("/ws")
+            .register(MyWebsocketHandler.class);
+
+        server.start();
+    }
+}

Interacting with Websockets sessions

The WebSocketSession object provides methods to interact with the Websocket session, such as sending messages, closing the connection, and getting the remote address and session ID.

+ + + + \ No newline at end of file diff --git a/hashmap.json b/hashmap.json index fd344a7..23a986d 100644 --- a/hashmap.json +++ b/hashmap.json @@ -1 +1 @@ -{"flash_index.md":"BZydQnCn","flash_installation.md":"BILQfhgS","flash_request-handler.md":"Bh6_Xj69","flash_request-response.md":"gIrPmRQZ","flash_server-router.md":"DthG-_ys","flash_server-types.md":"Bq9J39sx","index.md":"CKSOJSRs","mobot_index.md":"CdigA9Ng","serverlibraries_index.md":"CnbDNesW"} +{"flash_handler-default-implementation.md":"CL5VF6xH","flash_index.md":"E5zPbZtg","flash_installation.md":"6Deie__C","flash_request-handler.md":"D-YKsT8g","flash_request-response.md":"AHTHQW77","flash_server-router.md":"bhWEamQT","flash_websockets.md":"DzzW1sdE","index.md":"CgmTRI0Q","mobot_index.md":"Be0Zoetq","serverlibraries_index.md":"CeIqSPIs"} diff --git a/index.html b/index.html index bf2af6b..2db4caa 100644 --- a/index.html +++ b/index.html @@ -9,11 +9,11 @@ - + - - - + + + @@ -26,7 +26,7 @@ - + \ No newline at end of file diff --git a/mobot/index.html b/mobot/index.html index a863d56..1d26384 100644 --- a/mobot/index.html +++ b/mobot/index.html @@ -9,11 +9,11 @@ - + - - - + + + @@ -26,7 +26,7 @@
Skip to content
- + \ No newline at end of file diff --git a/serverlibraries/index.html b/serverlibraries/index.html index e4fc328..78d5ae7 100644 --- a/serverlibraries/index.html +++ b/serverlibraries/index.html @@ -9,11 +9,11 @@ - + - - - + + + @@ -26,7 +26,7 @@
Skip to content
- + \ No newline at end of file