diff --git a/404.html b/404.html new file mode 100644 index 0000000..7f21c90 --- /dev/null +++ b/404.html @@ -0,0 +1,20 @@ + + + + + + Shardcake + + + + + + + + +

404

How did we get here?
+ Take me home. +
+ + + diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..0fec4cb --- /dev/null +++ b/about/index.html @@ -0,0 +1,32 @@ + + + + + + About | Shardcake + + + + + + + + +

# About

Shardcake is a project developed at Devsisters (opens new window) by the team behind the popular game Cookie Run: Kingdom (opens new window). +We have been successfully using this project in production since March 2022.

If you happen to be in South Korea, we are recruiting (opens new window)!

Shardcake was developed after years of experience with Akka Cluster Sharding (opens new window). +Even though code has nothing in common with Akka, credits go to them for some ideas and concepts.

kingdom image

+ + + diff --git a/arch.png b/arch.png new file mode 100644 index 0000000..772ac30 Binary files /dev/null and b/arch.png differ diff --git a/arch2.png b/arch2.png new file mode 100644 index 0000000..24078c9 Binary files /dev/null and b/arch2.png differ diff --git a/assets/css/0.styles.65c88f21.css b/assets/css/0.styles.65c88f21.css new file mode 100644 index 0000000..7a93415 --- /dev/null +++ b/assets/css/0.styles.65c88f21.css @@ -0,0 +1 @@ +code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}.theme-default-content code{color:#476582;padding:.25rem .5rem;margin:0;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.theme-default-content code .token.deleted{color:#ec5975}.theme-default-content code .token.inserted{color:#fd7622}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;background-color:#282c34;border-radius:6px;overflow:auto}.theme-default-content pre[class*=language-] code,.theme-default-content pre code{color:#fff;padding:0;background-color:transparent;border-radius:0}div[class*=language-]{position:relative;background-color:#282c34;border-radius:6px}div[class*=language-] .highlight-lines{-webkit-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlighted{background-color:rgba(0,0,0,.66)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent;position:relative;z-index:1}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:hsla(0,0%,100%,.4)}div[class*=language-]:not(.line-numbers-mode) .line-numbers-wrapper{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlighted{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlighted:before{content:" ";position:absolute;z-index:3;left:0;top:0;display:block;width:3.5rem;height:100%;background-color:rgba(0,0,0,.66)}div[class*=language-].line-numbers-mode pre{padding-left:4.5rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers-wrapper{position:absolute;top:0;width:3.5rem;text-align:center;color:hsla(0,0%,100%,.3);padding:1.25rem 0;line-height:1.4}div[class*=language-].line-numbers-mode .line-numbers-wrapper br{-webkit-user-select:none;user-select:none}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number{position:relative;z-index:4;-webkit-user-select:none;user-select:none;font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;z-index:2;top:0;left:0;width:3.5rem;height:100%;border-radius:6px 0 0 6px;border-right:1px solid rgba(0,0,0,.66);background-color:#282c34}div[class~=language-js]:before{content:"js"}div[class~=language-ts]:before{content:"ts"}div[class~=language-html]:before{content:"html"}div[class~=language-md]:before{content:"md"}div[class~=language-vue]:before{content:"vue"}div[class~=language-css]:before{content:"css"}div[class~=language-sass]:before{content:"sass"}div[class~=language-scss]:before{content:"scss"}div[class~=language-less]:before{content:"less"}div[class~=language-stylus]:before{content:"stylus"}div[class~=language-go]:before{content:"go"}div[class~=language-java]:before{content:"java"}div[class~=language-c]:before{content:"c"}div[class~=language-sh]:before{content:"sh"}div[class~=language-yaml]:before{content:"yaml"}div[class~=language-py]:before{content:"py"}div[class~=language-docker]:before{content:"docker"}div[class~=language-dockerfile]:before{content:"dockerfile"}div[class~=language-makefile]:before{content:"makefile"}div[class~=language-javascript]:before{content:"js"}div[class~=language-typescript]:before{content:"ts"}div[class~=language-markup]:before{content:"html"}div[class~=language-markdown]:before{content:"md"}div[class~=language-json]:before{content:"json"}div[class~=language-ruby]:before{content:"rb"}div[class~=language-python]:before{content:"py"}div[class~=language-bash]:before{content:"sh"}div[class~=language-php]:before{content:"php"}.custom-block .custom-block-title{font-weight:600;margin-bottom:-.4rem}.custom-block.danger,.custom-block.tip,.custom-block.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#f3f5f7;border-color:#42b983}.custom-block.warning{background-color:rgba(255,229,100,.3);border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:#2c3e50}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:#2c3e50}.custom-block.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:#eee}.custom-block.details h4{margin-top:0}.custom-block.details figure:last-child,.custom-block.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-block.details summary{outline:none;cursor:pointer}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-bottom:6px solid #ccc}.arrow.down,.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent}.arrow.down{border-top:6px solid #ccc}.arrow.right{border-left:6px solid #ccc}.arrow.left,.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent}.arrow.left{border-right:6px solid #ccc}.theme-default-content:not(.custom){max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.theme-default-content:not(.custom){padding:2rem}}@media (max-width:419px){.theme-default-content:not(.custom){padding:1.5rem}}.table-of-contents .badge{vertical-align:middle}body,html{padding:0;margin:0;background-color:#fff}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px;color:#2c3e50}.page{padding-left:20rem}.navbar{z-index:20;right:0;height:3.6rem;background-color:#fff;box-sizing:border-box;border-bottom:1px solid #eaecef}.navbar,.sidebar-mask{position:fixed;top:0;left:0}.sidebar-mask{z-index:9;width:100vw;height:100vh;display:none}.sidebar{font-size:16px;background-color:#fff;width:20rem;position:fixed;z-index:10;margin:0;top:3.6rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid #eaecef;overflow-y:auto}.theme-default-content:not(.custom)>:first-child{margin-top:3.6rem}.theme-default-content:not(.custom) a:hover{text-decoration:underline}.theme-default-content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.theme-default-content:not(.custom) img{max-width:100%}.theme-default-content.custom{padding:0;margin:0}.theme-default-content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#fd7622}p a code{font-weight:400}kbd{background:#eee;border:.15rem solid #ddd;border-bottom:.25rem solid #ddd;border-radius:.15rem;padding:0 .15em}blockquote{font-size:1rem;color:#999;border-left:.2rem solid #dfe2e5;margin:1rem 0;padding:.25rem 0 .25rem 1rem}blockquote>p{margin:0}ol,ul{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}.theme-default-content:not(.custom)>h1,.theme-default-content:not(.custom)>h2,.theme-default-content:not(.custom)>h3,.theme-default-content:not(.custom)>h4,.theme-default-content:not(.custom)>h5,.theme-default-content:not(.custom)>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.theme-default-content:not(.custom)>h1:first-child,.theme-default-content:not(.custom)>h2:first-child,.theme-default-content:not(.custom)>h3:first-child,.theme-default-content:not(.custom)>h4:first-child,.theme-default-content:not(.custom)>h5:first-child,.theme-default-content:not(.custom)>h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.theme-default-content:not(.custom)>h1:first-child+.custom-block,.theme-default-content:not(.custom)>h1:first-child+p,.theme-default-content:not(.custom)>h1:first-child+pre,.theme-default-content:not(.custom)>h2:first-child+.custom-block,.theme-default-content:not(.custom)>h2:first-child+p,.theme-default-content:not(.custom)>h2:first-child+pre,.theme-default-content:not(.custom)>h3:first-child+.custom-block,.theme-default-content:not(.custom)>h3:first-child+p,.theme-default-content:not(.custom)>h3:first-child+pre,.theme-default-content:not(.custom)>h4:first-child+.custom-block,.theme-default-content:not(.custom)>h4:first-child+p,.theme-default-content:not(.custom)>h4:first-child+pre,.theme-default-content:not(.custom)>h5:first-child+.custom-block,.theme-default-content:not(.custom)>h5:first-child+p,.theme-default-content:not(.custom)>h5:first-child+pre,.theme-default-content:not(.custom)>h6:first-child+.custom-block,.theme-default-content:not(.custom)>h6:first-child+p,.theme-default-content:not(.custom)>h6:first-child+pre{margin-top:2rem}h1:focus .header-anchor,h1:hover .header-anchor,h2:focus .header-anchor,h2:hover .header-anchor,h3:focus .header-anchor,h3:hover .header-anchor,h4:focus .header-anchor,h4:hover .header-anchor,h5:focus .header-anchor,h5:hover .header-anchor,h6:focus .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid #eaecef}h3{font-size:1.35rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;-webkit-user-select:none;user-select:none;opacity:0}a.header-anchor:focus,a.header-anchor:hover{text-decoration:none}.line-number,code,kbd{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}ol,p,ul{line-height:1.7}hr{border:0;border-top:1px solid #eaecef}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto}tr{border-top:1px solid #dfe2e5}tr:nth-child(2n){background-color:#f6f8fa}td,th{border:1px solid #dfe2e5;padding:.6em 1em}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .theme-default-content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .sidebar{top:0}@media (min-width:720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}@media (max-width:959px){.sidebar{font-size:15px;width:16.4rem}.page{padding-left:16.4rem}}@media (max-width:719px){.sidebar{top:0;padding-top:3.6rem;transform:translateX(-100%);transition:transform .2s ease}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.home .hero img{max-height:200px!important}#nprogress{pointer-events:none}#nprogress .bar{background:#fd7622;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #fd7622,0 0 5px #fd7622;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border-color:#fd7622 transparent transparent #fd7622;border-style:solid;border-width:2px;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.icon.outbound{color:#aaa;display:inline-block;vertical-align:middle;position:relative;top:-1px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.algolia-search-wrapper>span{vertical-align:middle}.algolia-search-wrapper .algolia-autocomplete{line-height:normal}.algolia-search-wrapper .algolia-autocomplete .ds-dropdown-menu{background-color:#fff;border:1px solid #999;border-radius:4px;font-size:16px;margin:6px 0 0;padding:4px;text-align:left}.algolia-search-wrapper .algolia-autocomplete .ds-dropdown-menu:before{border-color:#999}.algolia-search-wrapper .algolia-autocomplete .ds-dropdown-menu [class*=ds-dataset-]{border:none;padding:0}.algolia-search-wrapper .algolia-autocomplete .ds-dropdown-menu .ds-suggestions{margin-top:0}.algolia-search-wrapper .algolia-autocomplete .ds-dropdown-menu .ds-suggestion{border-bottom:1px solid #eaecef}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion--highlight{color:#2c815b}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion{border-color:#eaecef;padding:0}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--category-header{padding:5px 10px;margin-top:0;background:#fd7622;color:#fff;font-weight:600}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--highlight{background:hsla(0,0%,100%,.6)}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--wrapper{padding:0}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--title{font-weight:600;margin-bottom:0;color:#2c3e50}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{vertical-align:top;padding:5px 7px 5px 5px;border-color:#eaecef;background:#f1f3f5}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after{display:none}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column-text{color:#555}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-footer{border-color:#eaecef}.algolia-search-wrapper .algolia-autocomplete .ds-cursor .algolia-docsearch-suggestion--content{background-color:#e7edf3!important;color:#2c3e50}@media (min-width:719px){.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{float:none;width:150px;min-width:150px;display:table-cell}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content{float:none;display:table-cell;width:100%;vertical-align:top}.algolia-search-wrapper .algolia-autocomplete .algolia-docsearch-suggestion .ds-dropdown-menu{min-width:515px!important}}@media (max-width:719px){.algolia-search-wrapper .ds-dropdown-menu{min-width:calc(100vw - 4rem)!important;max-width:calc(100vw - 4rem)!important}.algolia-search-wrapper .algolia-docsearch-suggestion--wrapper{padding:5px 7px 5px 5px!important}.algolia-search-wrapper .algolia-docsearch-suggestion--subcategory-column{padding:0!important;background:#fff!important}.algolia-search-wrapper .algolia-docsearch-suggestion--subcategory-column-text:after{content:" > ";font-size:10px;line-height:14.4px;display:inline-block;width:5px;margin:-3px 3px 0;vertical-align:middle}}.home{padding:3.6rem 2rem 0;max-width:960px;margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:#6a8bad}.home .hero .action-button{display:inline-block;font-size:1.2rem;color:#fff;background-color:#fd7622;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #fd6406}.home .hero .action-button:hover{background-color:#fd8438}.home .features{border-top:1px solid #eaecef;padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:#3a5169}.home .feature p{color:#4e6e8e}.home .footer{padding:2.5rem;border-top:1px solid #eaecef;text-align:center;color:#4e6e8e}@media (max-width:719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.search-box{display:inline-block;position:relative;margin-right:1rem}.search-box input{cursor:text;width:10rem;height:2rem;color:#4e6e8e;display:inline-block;border:1px solid #cfd4db;border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all .2s ease;background:#fff url(/shardcake/assets/img/search.83621669.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#fd7622}.search-box .suggestions{background:#fff;width:20rem;position:absolute;top:2rem;border:1px solid #cfd4db;border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestions.align-right{right:0}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:#5d82a6}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused{background-color:#f3f4f5}.search-box .suggestion.focused a{color:#fd7622}@media (max-width:959px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (-ms-high-contrast:none){.search-box input{height:2rem}}@media (max-width:959px) and (min-width:719px){.search-box .suggestions{left:0}}@media (max-width:719px){.search-box{margin-right:0}.search-box input{left:1rem}.search-box .suggestions{right:0}}@media (max-width:419px){.search-box .suggestions{width:calc(100vw - 4rem)}.search-box input:focus{width:8rem}}.sidebar-button{cursor:pointer;display:none;width:1.25rem;height:1.25rem;position:absolute;padding:.6rem;top:.6rem;left:1rem}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (max-width:719px){.sidebar-button{display:block}}.dropdown-enter,.dropdown-leave-to{height:0!important}.badge[data-v-15b7b770]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff}.badge.green[data-v-15b7b770],.badge.tip[data-v-15b7b770],.badge[data-v-15b7b770]{background-color:#42b983}.badge.error[data-v-15b7b770]{background-color:#da5961}.badge.warn[data-v-15b7b770],.badge.warning[data-v-15b7b770],.badge.yellow[data-v-15b7b770]{background-color:#e7c000}.badge+.badge[data-v-15b7b770]{margin-left:5px}.theme-code-block[data-v-759a7d02]{display:none}.theme-code-block__active[data-v-759a7d02]{display:block}.theme-code-block>pre[data-v-759a7d02]{background-color:orange}.theme-code-group__nav[data-v-deefee04]{margin-bottom:-35px;background-color:#282c34;padding-bottom:22px;border-top-left-radius:6px;border-top-right-radius:6px;padding-left:10px;padding-top:10px}.theme-code-group__ul[data-v-deefee04]{margin:auto 0;padding-left:0;display:inline-flex;list-style:none}.theme-code-group__nav-tab[data-v-deefee04]{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:hsla(0,0%,100%,.9);font-weight:600}.theme-code-group__nav-tab-active[data-v-deefee04]{border-bottom:1px solid #42b983}.pre-blank[data-v-deefee04]{color:#42b983}.searchbox{display:inline-block;position:relative;width:200px;height:32px!important;white-space:nowrap;box-sizing:border-box;visibility:visible!important}.searchbox .algolia-autocomplete{display:block;width:100%;height:100%}.searchbox__wrapper{width:100%;height:100%;z-index:999;position:relative}.searchbox__input{display:inline-block;box-sizing:border-box;transition:box-shadow .4s ease,background .4s ease;border:0;border-radius:16px;box-shadow:inset 0 0 0 1px #ccc;background:#fff!important;padding:0 26px 0 32px;width:100%;height:100%;vertical-align:middle;white-space:normal;font-size:12px;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbox__input::-webkit-search-cancel-button,.searchbox__input::-webkit-search-decoration,.searchbox__input::-webkit-search-results-button,.searchbox__input::-webkit-search-results-decoration{display:none}.searchbox__input:hover{box-shadow:inset 0 0 0 1px #b3b3b3}.searchbox__input:active,.searchbox__input:focus{outline:0;box-shadow:inset 0 0 0 1px #aaa;background:#fff}.searchbox__input::-moz-placeholder{color:#aaa}.searchbox__input::placeholder{color:#aaa}.searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69,142,225,0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;user-select:none;right:inherit;left:0}.searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""}.searchbox__submit:active,.searchbox__submit:hover{cursor:pointer}.searchbox__submit:focus{outline:0}.searchbox__submit svg{width:14px;height:14px;vertical-align:middle;fill:#6d7e96}.searchbox__reset{display:block;position:absolute;top:8px;right:8px;margin:0;border:0;background:none;cursor:pointer;padding:0;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;user-select:none;fill:rgba(0,0,0,.5)}.searchbox__reset.hide{display:none}.searchbox__reset:focus{outline:0}.searchbox__reset svg{display:block;margin:4px;width:8px;height:8px}.searchbox__input:valid~.searchbox__reset{display:block;animation-name:sbx-reset-in;animation-duration:.15s}@keyframes sbx-reset-in{0%{transform:translate3d(-20%,0,0);opacity:0}to{transform:none;opacity:1}}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu{right:0!important;left:inherit!important}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before{right:48px}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu{left:0!important;right:inherit!important}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before{left:48px}.algolia-autocomplete .ds-dropdown-menu{top:-6px;border-radius:4px;margin:6px 0 0;padding:0;text-align:left;height:auto;position:relative;background:transparent;border:none;z-index:999;max-width:600px;min-width:500px;box-shadow:0 1px 0 0 rgba(0,0,0,.2),0 2px 3px 0 rgba(0,0,0,.1)}.algolia-autocomplete .ds-dropdown-menu:before{display:block;position:absolute;content:"";width:14px;height:14px;background:#fff;z-index:1000;top:-7px;border-top:1px solid #d9d9d9;border-right:1px solid #d9d9d9;transform:rotate(-45deg);border-radius:2px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions{position:relative;z-index:1000;margin-top:8px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions a:hover{text-decoration:none}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion{cursor:pointer}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple,.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content{background-color:rgba(69,142,225,.05)}.algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-]{position:relative;border:1px solid #d9d9d9;background:#fff;border-radius:4px;overflow:auto;padding:0 8px 8px}.algolia-autocomplete .ds-dropdown-menu *{box-sizing:border-box}.algolia-autocomplete .algolia-docsearch-suggestion{display:block;position:relative;padding:0 8px;background:#fff;color:#02060c;overflow:hidden}.algolia-autocomplete .algolia-docsearch-suggestion--highlight{color:#174d8c;background:rgba(143,187,237,.1);padding:.1em .05em}.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{padding:0 0 1px;background:inherit;box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--content{display:block;float:right;width:70%;position:relative;padding:5.33333px 0 5.33333px 10.66667px;cursor:pointer}.algolia-autocomplete .algolia-docsearch-suggestion--content:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;left:-1px}.algolia-autocomplete .algolia-docsearch-suggestion--category-header{position:relative;border-bottom:1px solid #ddd;display:none;margin-top:8px;padding:4px 0;font-size:1em;color:#33363d}.algolia-autocomplete .algolia-docsearch-suggestion--wrapper{width:100%;float:left;padding:8px 0 0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column{float:left;width:30%;text-align:right;position:relative;padding:5.33333px 10.66667px;color:#a4a7ae;font-size:.9em;word-wrap:break-word}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;right:0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline{display:none}.algolia-autocomplete .algolia-docsearch-suggestion--title{margin-bottom:4px;color:#02060c;font-size:.9em;font-weight:700}.algolia-autocomplete .algolia-docsearch-suggestion--text{display:block;line-height:1.2em;font-size:.85em;color:#63676d}.algolia-autocomplete .algolia-docsearch-suggestion--no-results{width:100%;padding:8px 0;text-align:center;font-size:1.2em}.algolia-autocomplete .algolia-docsearch-suggestion--no-results:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion code{padding:1px 5px;font-size:90%;border:none;color:#222;background-color:#ebebeb;border-radius:3px;font-family:Menlo,Monaco,Consolas,Courier New,monospace}.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight{background:none}.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header,.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary{display:block}@media (min-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:block}}@media (max-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:inline-block;width:auto;float:left;padding:0;color:#02060c;font-size:.9em;font-weight:700;text-align:left;opacity:.5}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after{content:"|"}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content{display:inline-block;width:auto;text-align:left;float:left;padding:0}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content:before{display:none}}.algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion{border-bottom:1px solid #eee;padding:8px;margin:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content{width:100%;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content:before{display:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header{margin:0;padding:0;display:block;width:100%;border:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1{opacity:.6;font-size:.85em}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,');content:"";width:10px;height:10px;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper{width:100%;float:left;margin:0;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline{display:none!important}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title{margin:0;color:#458ee1;font-size:.9em;font-weight:400}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title:before{content:"#";font-weight:700;color:#458ee1;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text{margin:4px 0 0;display:block;line-height:1.4em;padding:5.33333px 8px;background:#f8f8f8;font-size:.85em;opacity:.8}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{color:#3f4145;font-weight:700;box-shadow:none}.algolia-autocomplete .algolia-docsearch-footer{width:134px;height:20px;z-index:2000;margin-top:10.66667px;float:right;font-size:0;line-height:0}.algolia-autocomplete .algolia-docsearch-footer--logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='168' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M78.988.938h16.594a2.968 2.968 0 012.966 2.966V20.5a2.967 2.967 0 01-2.966 2.964H78.988a2.967 2.967 0 01-2.966-2.964V3.897A2.961 2.961 0 0178.988.938zm41.937 17.866c-4.386.02-4.386-3.54-4.386-4.106l-.007-13.336 2.675-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-10.846-2.18c.821 0 1.43-.047 1.855-.129v-2.719a6.334 6.334 0 00-1.574-.199 5.7 5.7 0 00-.897.069 2.699 2.699 0 00-.814.24c-.24.116-.439.28-.582.491-.15.212-.219.335-.219.656 0 .628.219.991.616 1.23s.938.362 1.615.362zm-.233-9.7c.883 0 1.629.109 2.231.328.602.218 1.088.525 1.444.915.363.396.609.922.76 1.483.157.56.232 1.175.232 1.85v6.874a32.5 32.5 0 01-1.868.314c-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 01-1.471-.636 3.085 3.085 0 01-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525a3.24 3.24 0 011.047-1.106c.445-.287.95-.492 1.532-.615a8.8 8.8 0 011.82-.185 8.404 8.404 0 011.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 00-.384-.73 1.784 1.784 0 00-.724-.493 3.164 3.164 0 00-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 00-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 012.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 00-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 00-.814.24 1.46 1.46 0 00-.582.491c-.15.212-.218.335-.218.656 0 .628.218.991.615 1.23.404.245.938.362 1.615.362zm-.226-9.694c.883 0 1.629.108 2.231.327.602.219 1.088.526 1.444.915.355.39.609.923.759 1.483a6.8 6.8 0 01.233 1.852v6.873c-.41.088-1.034.19-1.868.314-.834.123-1.772.184-2.813.184-.69 0-1.327-.068-1.895-.198a4.001 4.001 0 01-1.471-.635 3.085 3.085 0 01-.951-1.134c-.226-.465-.343-1.12-.343-1.804 0-.656.13-1.073.384-1.524.26-.45.608-.82 1.047-1.107.445-.286.95-.491 1.532-.614a8.803 8.803 0 012.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 00-.109-.875 1.873 1.873 0 00-.384-.731 1.784 1.784 0 00-.724-.492 3.165 3.165 0 00-1.143-.205c-.616 0-1.177.075-1.69.164a7.75 7.75 0 00-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 012.073-.177zm-8.034-1.271a1.626 1.626 0 01-1.628-1.62c0-.895.725-1.62 1.628-1.62.904 0 1.63.725 1.63 1.62 0 .895-.733 1.62-1.63 1.62zm1.348 13.22h-2.689V7.27l2.69-.423v11.956zm-4.714 0c-4.386.02-4.386-3.54-4.386-4.107l-.008-13.336 2.676-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-8.698-5.903c0-1.156-.253-2.119-.746-2.788-.493-.677-1.183-1.01-2.067-1.01-.882 0-1.574.333-2.065 1.01-.493.676-.733 1.632-.733 2.788 0 1.168.246 1.953.74 2.63.492.683 1.183 1.018 2.066 1.018.882 0 1.574-.342 2.067-1.019.492-.683.738-1.46.738-2.63zm2.737-.007c0 .902-.13 1.584-.397 2.33a5.52 5.52 0 01-1.128 1.906 4.986 4.986 0 01-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 01-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 01-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 011.15-1.892 5.133 5.133 0 011.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423a4.88 4.88 0 011.753 1.216 5.644 5.644 0 011.135 1.892c.287.738.431 1.606.431 2.508zm-20.138 0c0 1.12.246 2.363.738 2.882.493.52 1.13.78 1.91.78.424 0 .828-.062 1.204-.178.377-.116.677-.253.917-.417V9.33a10.476 10.476 0 00-1.766-.226c-.971-.028-1.71.37-2.23 1.004-.513.636-.773 1.75-.773 2.788zm7.438 5.274c0 1.824-.466 3.156-1.404 4.004-.936.846-2.367 1.27-4.296 1.27-.705 0-2.17-.137-3.34-.396l.431-2.118c.98.205 2.272.26 2.95.26 1.074 0 1.84-.219 2.299-.656.459-.437.684-1.086.684-1.948v-.437a8.07 8.07 0 01-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 01-1.567-1.025c-.431-.45-.774-1.017-1.013-1.694-.24-.677-.363-1.885-.363-2.773 0-.834.13-1.88.384-2.577.26-.696.629-1.298 1.129-1.796.493-.498 1.095-.881 1.8-1.162a6.605 6.605 0 012.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17z' fill='%235468FF'/%3E%3Cpath d='M6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 00-.502 1.03c0 .425.164.765.494 1.02.33.256.835.532 1.516.83.447.192.795.356 1.045.495.25.138.537.332.862.582.324.25.563.548.718.894.154.345.23.741.23 1.188 0 .947-.334 1.691-1.004 2.234-.67.542-1.537.814-2.601.814-1.18 0-2.16-.229-2.936-.686v-1.708c.84.628 1.814.942 2.92.942.585 0 1.048-.136 1.388-.407.34-.271.51-.646.51-1.125 0-.287-.1-.55-.302-.79-.203-.24-.42-.42-.655-.542-.234-.123-.585-.29-1.053-.503a61.27 61.27 0 01-.582-.271 13.67 13.67 0 01-.55-.287 4.275 4.275 0 01-.567-.351 6.92 6.92 0 01-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 01-.104-.742c0-.915.333-1.638.998-2.17.664-.532 1.523-.798 2.576-.798.968 0 1.793.17 2.473.51zm7.468 5.696v-.287c-.022-.607-.187-1.088-.495-1.444-.309-.357-.75-.535-1.324-.535-.532 0-.99.194-1.373.583-.382.388-.622.949-.717 1.683h3.909zm1.005 2.792v1.404c-.596.34-1.383.51-2.362.51-1.255 0-2.255-.377-3-1.132-.744-.755-1.116-1.744-1.116-2.968 0-1.297.34-2.316 1.021-3.055.68-.74 1.548-1.11 2.6-1.11 1.033 0 1.852.323 2.458.966.606.644.91 1.572.91 2.784 0 .33-.033.676-.096 1.038h-5.314c.107.702.405 1.239.894 1.611.49.372 1.106.558 1.85.558.862 0 1.58-.202 2.155-.606zm6.605-1.77h-1.212c-.596 0-1.045.116-1.349.35-.303.234-.454.532-.454.894 0 .372.117.664.35.877.235.213.575.32 1.022.32.51 0 .912-.142 1.204-.424.293-.281.44-.651.44-1.108v-.91zm-4.068-2.554V9.325c.627-.361 1.457-.542 2.489-.542 2.116 0 3.175 1.026 3.175 3.08V17h-1.548v-.957c-.415.68-1.143 1.02-2.186 1.02-.766 0-1.38-.22-1.843-.661-.462-.442-.694-1.003-.694-1.684 0-.776.293-1.38.878-1.81.585-.431 1.404-.647 2.457-.647h1.34V11.8c0-.554-.133-.971-.399-1.253-.266-.282-.707-.423-1.324-.423a4.07 4.07 0 00-2.345.718zm9.333-1.93v1.42c.394-1 1.101-1.5 2.123-1.5.148 0 .313.016.494.048v1.531a1.885 1.885 0 00-.75-.143c-.542 0-.989.24-1.34.718-.351.479-.527 1.048-.527 1.707V17h-1.563V8.91h1.563zm5.01 4.084c.022.82.272 1.492.75 2.019.479.526 1.15.79 2.01.79.639 0 1.235-.176 1.788-.527v1.404c-.521.319-1.186.479-1.995.479-1.265 0-2.276-.4-3.031-1.197-.755-.798-1.133-1.792-1.133-2.984 0-1.16.38-2.151 1.14-2.975.761-.825 1.79-1.237 3.088-1.237.702 0 1.346.149 1.93.447v1.436a3.242 3.242 0 00-1.77-.495c-.84 0-1.513.266-2.019.798-.505.532-.758 1.213-.758 2.042zM40.24 5.72v4.579c.458-1 1.293-1.5 2.505-1.5.787 0 1.42.245 1.899.734.479.49.718 1.17.718 2.042V17h-1.564v-5.106c0-.553-.14-.98-.422-1.284-.282-.303-.652-.455-1.11-.455-.531 0-1.002.202-1.411.606-.41.405-.615 1.022-.615 1.851V17h-1.563V5.72h1.563zm14.966 10.02c.596 0 1.096-.253 1.5-.758.404-.506.606-1.157.606-1.955 0-.915-.202-1.62-.606-2.114-.404-.495-.92-.742-1.548-.742-.553 0-1.05.224-1.491.67-.442.447-.662 1.133-.662 2.058 0 .958.212 1.67.638 2.138.425.469.946.703 1.563.703zM53.004 5.72v4.42c.574-.894 1.388-1.341 2.44-1.341 1.022 0 1.857.383 2.506 1.149.649.766.973 1.781.973 3.047 0 1.138-.309 2.109-.925 2.912-.617.803-1.463 1.205-2.537 1.205-1.075 0-1.894-.447-2.457-1.34V17h-1.58V5.72h1.58zm9.908 11.104l-3.223-7.913h1.739l1.005 2.632 1.26 3.415c.096-.32.48-1.458 1.15-3.415l.909-2.632h1.66l-2.92 7.866c-.777 2.074-1.963 3.11-3.559 3.11a2.92 2.92 0 01-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z' fill='%235D6494'/%3E%3Cpath d='M89.632 5.967v-.772a.978.978 0 00-.978-.977h-2.28a.978.978 0 00-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 011.984-.28c.65 0 1.295.088 1.917.259.082.02.164-.04.164-.13m-6.248 1.01l-.39-.389a.977.977 0 00-1.382 0l-.465.465a.973.973 0 000 1.38l.383.383c.062.061.15.047.205-.014.226-.307.472-.601.746-.874.281-.28.568-.526.883-.751.068-.042.075-.137.02-.2m4.16 2.453v3.341c0 .096.104.165.192.117l2.97-1.537c.068-.034.089-.117.055-.184a3.695 3.695 0 00-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 01-4.49-4.482 4.488 4.488 0 014.49-4.482 4.488 4.488 0 014.489 4.482 4.484 4.484 0 01-4.49 4.482m0-10.85a6.363 6.363 0 100 12.729 6.37 6.37 0 006.372-6.368 6.358 6.358 0 00-6.371-6.36' fill='%23FFF'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;background-position:50%;background-size:100%;overflow:hidden;text-indent:-9000px;padding:0!important;width:100%;height:100%;display:block}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .dropdown-title,.dropdown-wrapper .mobile-dropdown-title{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:#2c3e50}.dropdown-wrapper .dropdown-title:hover,.dropdown-wrapper .mobile-dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow,.dropdown-wrapper .mobile-dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .mobile-dropdown-title{display:none;font-weight:600}.dropdown-wrapper .mobile-dropdown-title font-size inherit:hover{color:#fd7622}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item h4{margin:.45rem 0 0;border-top:1px solid #eee;padding:1rem 1.5rem .45rem 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover{color:#fd7622}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid #fd7622;border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4{margin-top:0;padding-top:0;border-top:0}@media (max-width:719px){.dropdown-wrapper.open .dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .dropdown-title{display:none}.dropdown-wrapper .mobile-dropdown-title{display:block}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item h4{border-top:0;margin-top:0;padding-top:0}.dropdown-wrapper .nav-dropdown .dropdown-item>a,.dropdown-wrapper .nav-dropdown .dropdown-item h4{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:719px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper.open .nav-dropdown,.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:#fff;padding:.6rem 0;border:1px solid;border-color:#ddd #ddd #ccc;text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.nav-links{display:inline-block}.nav-links a{line-height:1.4rem;color:inherit}.nav-links a.router-link-active,.nav-links a:hover{color:#fd7622}.nav-links .nav-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:2rem}.nav-links .nav-item:first-child{margin-left:0}.nav-links .repo-link{margin-left:1.5rem}@media (max-width:719px){.nav-links .nav-item,.nav-links .repo-link{margin-left:0}}@media (min-width:719px){.nav-links a.router-link-active,.nav-links a:hover{color:#2c3e50}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid #fd8134}}.navbar{padding:.7rem 1.5rem;line-height:2.2rem}.navbar a,.navbar img,.navbar span{display:inline-block}.navbar .logo{height:2.2rem;min-width:2.2rem;margin-right:.8rem;vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:#2c3e50;position:relative}.navbar .links{padding-left:1.5rem;box-sizing:border-box;background-color:#fff;white-space:nowrap;font-size:.9rem;position:absolute;right:1.5rem;top:.7rem;display:flex}.navbar .links .search-box{flex:0 0 auto;vertical-align:top}@media (max-width:719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}.navbar .links{padding-left:1.5rem}.navbar .site-name{width:calc(100vw - 9.4rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}}.page-edit{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-edit{padding:2rem}}@media (max-width:419px){.page-edit{padding:1.5rem}}.page-edit{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block}.page-edit .edit-link a{color:#4e6e8e;margin-right:.25rem}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:#4e6e8e}.page-edit .last-updated .time{font-weight:400;color:#767676}@media (max-width:719px){.page-edit .edit-link{margin-bottom:.5rem}.page-edit .last-updated{font-size:.8em;float:none;text-align:left}}.page-nav{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-nav{padding:2rem}}@media (max-width:419px){.page-nav{padding:1.5rem}}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid #eaecef;padding-top:1rem;overflow:auto}.page-nav .next{float:right}.page{padding-bottom:2rem;display:block}.sidebar-group .sidebar-group{padding-left:.5em}.sidebar-group:not(.collapsable) .sidebar-heading:not(.clickable){cursor:auto;color:inherit}.sidebar-group.is-sub-group{padding-left:0}.sidebar-group.is-sub-group>.sidebar-heading{font-size:.95em;line-height:1.4;font-weight:400;padding-left:2rem}.sidebar-group.is-sub-group>.sidebar-heading:not(.clickable){opacity:.5}.sidebar-group.is-sub-group>.sidebar-group-items{padding-left:1rem}.sidebar-group.is-sub-group>.sidebar-group-items>li>.sidebar-link{font-size:.95em;border-left:none}.sidebar-group.depth-2>.sidebar-heading{border-left:none}.sidebar-heading{color:#2c3e50;transition:color .15s ease;cursor:pointer;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0;border-left:.25rem solid transparent}.sidebar-heading.open,.sidebar-heading:hover{color:inherit}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-heading.clickable.active{font-weight:600;color:#fd7622;border-left-color:#fd7622}.sidebar-heading.clickable:hover{color:#fd7622}.sidebar-group-items{transition:height .1s ease-out;font-size:.95em;overflow:hidden}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}a.sidebar-link{font-size:1em;font-weight:400;display:inline-block;color:#2c3e50;border-left:.25rem solid transparent;padding:.35rem 1rem .35rem 1.25rem;line-height:1.4;width:100%;box-sizing:border-box}a.sidebar-link:hover{color:#fd7622}a.sidebar-link.active{font-weight:600;color:#fd7622;border-left-color:#fd7622}.sidebar-group a.sidebar-link{padding-left:2rem}.sidebar-sub-headers a.sidebar-link{padding-top:.25rem;padding-bottom:.25rem;border-left:none}.sidebar-sub-headers a.sidebar-link.active{font-weight:500}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .nav-links{display:none;border-bottom:1px solid #eaecef;padding:.5rem 0 .75rem}.sidebar .nav-links a{font-weight:600}.sidebar .nav-links .nav-item,.sidebar .nav-links .repo-link{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar>.sidebar-links{padding:1.5rem 0}.sidebar>.sidebar-links>li>a.sidebar-link{font-size:1.1em;line-height:1.7;font-weight:700}.sidebar>.sidebar-links>li:not(:first-child){margin-top:.75rem}@media (max-width:719px){.sidebar .nav-links{display:block}.sidebar .nav-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar>.sidebar-links{padding:1rem 0}} \ No newline at end of file diff --git a/assets/img/search.83621669.svg b/assets/img/search.83621669.svg new file mode 100644 index 0000000..03d8391 --- /dev/null +++ b/assets/img/search.83621669.svg @@ -0,0 +1 @@ + diff --git a/assets/js/1.dc7adba2.js b/assets/js/1.dc7adba2.js new file mode 100644 index 0000000..39ce33c --- /dev/null +++ b/assets/js/1.dc7adba2.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[1,12,14,19,20,21],{239:function(t,e,n){"use strict";n.d(e,"d",(function(){return s})),n.d(e,"a",(function(){return r})),n.d(e,"i",(function(){return o})),n.d(e,"f",(function(){return l})),n.d(e,"g",(function(){return u})),n.d(e,"h",(function(){return c})),n.d(e,"b",(function(){return h})),n.d(e,"e",(function(){return f})),n.d(e,"k",(function(){return p})),n.d(e,"l",(function(){return d})),n.d(e,"c",(function(){return m})),n.d(e,"j",(function(){return k}));n(90);const s=/#.*$/,i=/\.(md|html)$/,r=/\/$/,o=/^[a-z]+:/i;function a(t){return decodeURI(t).replace(s,"").replace(i,"")}function l(t){return o.test(t)}function u(t){return/^mailto:/.test(t)}function c(t){return/^tel:/.test(t)}function h(t){if(l(t))return t;const e=t.match(s),n=e?e[0]:"",i=a(t);return r.test(i)?t:i+".html"+n}function f(t,e){const n=decodeURIComponent(t.hash),i=function(t){const e=t.match(s);if(e)return e[0]}(e);if(i&&n!==i)return!1;return a(t.path)===a(e)}function p(t,e,n){if(l(e))return{type:"external",path:e};n&&(e=function(t,e,n){const s=t.charAt(0);if("/"===s)return t;if("?"===s||"#"===s)return e+t;const i=e.split("/");n&&i[i.length-1]||i.pop();const r=t.replace(/^\//,"").split("/");for(let t=0;tfunction t(e,n,s,i=1){if("string"==typeof e)return p(n,e,s);if(Array.isArray(e))return Object.assign(p(n,e[0],s),{title:e[1]});{const r=e.children||[];return 0===r.length&&e.path?Object.assign(p(n,e.path,s),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:r.map(e=>t(e,n,s,i+1)),collapsable:!1!==e.collapsable}}}(t,i,n)):[]}return[]}function g(t){const e=m(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:e.map(e=>({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}function m(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function k(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},240:function(t,e,n){},241:function(t,e,n){"use strict";n.r(e);var s=n(239),i={name:"NavLink",props:{item:{required:!0}},computed:{link(){return Object(s.b)(this.item.link)},exact(){return this.$site.locales?Object.keys(this.$site.locales).some(t=>t===this.link):"/"===this.link},isNonHttpURI(){return Object(s.g)(this.link)||Object(s.h)(this.link)},isBlankTarget(){return"_blank"===this.target},isInternal(){return!Object(s.f)(this.link)&&!this.isBlankTarget},target(){return this.isNonHttpURI?null:this.item.target?this.item.target:Object(s.f)(this.link)?"_blank":""},rel(){return this.isNonHttpURI||!1===this.item.rel?null:this.item.rel?this.item.rel:this.isBlankTarget?"noopener noreferrer":null}},methods:{focusoutAction(){this.$emit("focusout")}}},r=n(14),o=Object(r.a)(i,(function(){var t=this,e=t._self._c;return t.isInternal?e("RouterLink",{staticClass:"nav-link",attrs:{to:t.link,exact:t.exact},nativeOn:{focusout:function(e){return t.focusoutAction.apply(null,arguments)}}},[t._v("\n "+t._s(t.item.text)+"\n")]):e("a",{staticClass:"nav-link external",attrs:{href:t.link,target:t.target,rel:t.rel},on:{focusout:t.focusoutAction}},[t._v("\n "+t._s(t.item.text)+"\n "),t.isBlankTarget?e("OutboundLink"):t._e()],1)}),[],!1,null,null,null);e.default=o.exports},242:function(t,e,n){"use strict";n.r(e);var s={name:"DropdownTransition",methods:{setHeight(t){t.style.height=t.scrollHeight+"px"},unsetHeight(t){t.style.height=""}}},i=(n(243),n(14)),r=Object(i.a)(s,(function(){return(0,this._self._c)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.default=r.exports},243:function(t,e,n){"use strict";n(240)},244:function(t,e,n){},247:function(t,e,n){},251:function(t,e,n){"use strict";n(244)},254:function(t,e,n){"use strict";n.r(e);var s=n(241),i=n(242),r=n(91),o=n.n(r),a={name:"DropdownLink",components:{NavLink:s.default,DropdownTransition:i.default},props:{item:{required:!0}},data:()=>({open:!1}),computed:{dropdownAriaLabel(){return this.item.ariaLabel||this.item.text}},watch:{$route(){this.open=!1}},methods:{setOpen(t){this.open=t},isLastItemOfArray:(t,e)=>o()(e)===t,handleDropdown(){0===event.detail&&this.setOpen(!this.open)}}},l=(n(251),n(14)),u=Object(l.a)(a,(function(){var t=this,e=t._self._c;return e("div",{staticClass:"dropdown-wrapper",class:{open:t.open}},[e("button",{staticClass:"dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:t.handleDropdown}},[e("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),e("span",{staticClass:"arrow down"})]),t._v(" "),e("button",{staticClass:"mobile-dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:function(e){return t.setOpen(!t.open)}}},[e("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),e("span",{staticClass:"arrow",class:t.open?"down":"right"})]),t._v(" "),e("DropdownTransition",[e("ul",{directives:[{name:"show",rawName:"v-show",value:t.open,expression:"open"}],staticClass:"nav-dropdown"},t._l(t.item.items,(function(n,s){return e("li",{key:n.link||s,staticClass:"dropdown-item"},["links"===n.type?e("h4",[t._v("\n "+t._s(n.text)+"\n ")]):t._e(),t._v(" "),"links"===n.type?e("ul",{staticClass:"dropdown-subitem-wrapper"},t._l(n.items,(function(s){return e("li",{key:s.link,staticClass:"dropdown-subitem"},[e("NavLink",{attrs:{item:s},on:{focusout:function(e){t.isLastItemOfArray(s,n.items)&&t.isLastItemOfArray(n,t.item.items)&&t.setOpen(!1)}}})],1)})),0):e("NavLink",{attrs:{item:n},on:{focusout:function(e){t.isLastItemOfArray(n,t.item.items)&&t.setOpen(!1)}}})],1)})),0)])],1)}),[],!1,null,null,null);e.default=u.exports},257:function(t,e,n){"use strict";n(247)},258:function(t,e,n){},265:function(t,e,n){"use strict";n.r(e);var s=n(254),i=n(239),r={name:"NavLinks",components:{NavLink:n(241).default,DropdownLink:s.default},computed:{userNav(){return this.$themeLocaleConfig.nav||this.$site.themeConfig.nav||[]},nav(){const{locales:t}=this.$site;if(t&&Object.keys(t).length>1){const e=this.$page.path,n=this.$router.options.routes,s=this.$site.themeConfig.locales||{},i={text:this.$themeLocaleConfig.selectText||"Languages",ariaLabel:this.$themeLocaleConfig.ariaLabel||"Select language",items:Object.keys(t).map(i=>{const r=t[i],o=s[i]&&s[i].label||r.lang;let a;return r.lang===this.$lang?a=e:(a=e.replace(this.$localeConfig.path,i),n.some(t=>t.path===a)||(a=i)),{text:o,link:a}})};return[...this.userNav,i]}return this.userNav},userLinks(){return(this.nav||[]).map(t=>Object.assign(Object(i.j)(t),{items:(t.items||[]).map(i.j)}))},repoLink(){const{repo:t}=this.$site.themeConfig;return t?/^https?:/.test(t)?t:"https://github.com/"+t:null},repoLabel(){if(!this.repoLink)return;if(this.$site.themeConfig.repoLabel)return this.$site.themeConfig.repoLabel;const t=this.repoLink.match(/^https?:\/\/[^/]+/)[0],e=["GitHub","GitLab","Bitbucket"];for(let n=0;n{let s=i()(e,"title","");return i()(e,"frontmatter.tags")&&(s+=" "+e.frontmatter.tags.join(" ")),n&&(s+=" "+n),o(t,s)};const o=(t,e)=>{const n=t=>t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),s=new RegExp("[^\0-]"),i=t.split(/\s+/g).map(t=>t.trim()).filter(t=>!!t);if(s.test(t))return i.some(t=>e.toLowerCase().indexOf(t)>-1);{const s=t.endsWith(" ");return new RegExp(i.map((t,e)=>i.length!==e+1||s?`(?=.*\\b${n(t)}\\b)`:`(?=.*\\b${n(t)})`).join("")+".+","gi").test(e)}};var a={name:"SearchBox",data:()=>({query:"",focused:!1,focusIndex:0,placeholder:void 0}),computed:{showSuggestions(){return this.focused&&this.suggestions&&this.suggestions.length},suggestions(){const t=this.query.trim().toLowerCase();if(!t)return;const{pages:e}=this.$site,n=this.$site.themeConfig.searchMaxSuggestions||5,s=this.$localePath,i=[];for(let o=0;o=n);o++){const a=e[o];if(this.getPageLocalePath(a)===s&&this.isSearchable(a))if(r(t,a))i.push(a);else if(a.headers)for(let e=0;e=n);e++){const n=a.headers[e];n.title&&r(t,a,n.title)&&i.push(Object.assign({},a,{path:a.path+"#"+n.slug,header:n}))}}return i},alignRight(){return(this.$site.themeConfig.nav||[]).length+(this.$site.repo?1:0)<=2}},mounted(){this.placeholder=this.$site.themeConfig.searchPlaceholder||"",document.addEventListener("keydown",this.onHotkey)},beforeDestroy(){document.removeEventListener("keydown",this.onHotkey)},methods:{getPageLocalePath(t){for(const e in this.$site.locales||{})if("/"!==e&&0===t.path.indexOf(e))return e;return"/"},isSearchable(t){let e=null;return null===e||(e=Array.isArray(e)?e:new Array(e),e.filter(e=>t.path.match(e)).length>0)},onHotkey(t){t.srcElement===document.body&&["s","/"].includes(t.key)&&(this.$refs.input.focus(),t.preventDefault())},onUp(){this.showSuggestions&&(this.focusIndex>0?this.focusIndex--:this.focusIndex=this.suggestions.length-1)},onDown(){this.showSuggestions&&(this.focusIndex "+t._s(n.header.title))]):t._e()])])})),0):t._e()])}),[],!1,null,null,null).exports,c=n(283),h=n(265);function f(t,e){return t.ownerDocument.defaultView.getComputedStyle(t,null)[e]}var p={name:"Navbar",components:{SidebarButton:c.default,NavLinks:h.default,SearchBox:u,AlgoliaSearchBox:{}},data:()=>({linksWrapMaxWidth:null}),computed:{algolia(){return this.$themeLocaleConfig.algolia||this.$site.themeConfig.algolia||{}},isAlgoliaSearch(){return this.algolia&&this.algolia.apiKey&&this.algolia.indexName}},mounted(){const t=parseInt(f(this.$el,"paddingLeft"))+parseInt(f(this.$el,"paddingRight")),e=()=>{document.documentElement.clientWidth<719?this.linksWrapMaxWidth=null:this.linksWrapMaxWidth=this.$el.offsetWidth-t-(this.$refs.siteName&&this.$refs.siteName.offsetWidth||0)};e(),window.addEventListener("resize",e,!1)}},d=(n(290),Object(l.a)(p,(function(){var t=this,e=t._self._c;return e("header",{staticClass:"navbar"},[e("SidebarButton",{on:{"toggle-sidebar":function(e){return t.$emit("toggle-sidebar")}}}),t._v(" "),e("RouterLink",{staticClass:"home-link",attrs:{to:t.$localePath}},[t.$site.themeConfig.logo?e("img",{staticClass:"logo",attrs:{src:t.$withBase(t.$site.themeConfig.logo),alt:t.$siteTitle}}):t._e(),t._v(" "),t.$siteTitle?e("span",{ref:"siteName",staticClass:"site-name",class:{"can-hide":t.$site.themeConfig.logo}},[t._v(t._s(t.$siteTitle))]):t._e()]),t._v(" "),e("div",{staticClass:"links",style:t.linksWrapMaxWidth?{"max-width":t.linksWrapMaxWidth+"px"}:{}},[t.isAlgoliaSearch?e("AlgoliaSearchBox",{attrs:{options:t.algolia}}):!1!==t.$site.themeConfig.search&&!1!==t.$page.frontmatter.search?e("SearchBox"):t._e(),t._v(" "),e("NavLinks",{staticClass:"can-hide"})],1)],1)}),[],!1,null,null,null));e.default=d.exports}}]); \ No newline at end of file diff --git a/assets/js/10.a9ace829.js b/assets/js/10.a9ace829.js new file mode 100644 index 0000000..94381b3 --- /dev/null +++ b/assets/js/10.a9ace829.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[10,3,12,14,18,19,21],{239:function(t,e,n){"use strict";n.d(e,"d",(function(){return i})),n.d(e,"a",(function(){return r})),n.d(e,"i",(function(){return a})),n.d(e,"f",(function(){return l})),n.d(e,"g",(function(){return u})),n.d(e,"h",(function(){return c})),n.d(e,"b",(function(){return p})),n.d(e,"e",(function(){return h})),n.d(e,"k",(function(){return d})),n.d(e,"l",(function(){return f})),n.d(e,"c",(function(){return m})),n.d(e,"j",(function(){return g}));n(90);const i=/#.*$/,s=/\.(md|html)$/,r=/\/$/,a=/^[a-z]+:/i;function o(t){return decodeURI(t).replace(i,"").replace(s,"")}function l(t){return a.test(t)}function u(t){return/^mailto:/.test(t)}function c(t){return/^tel:/.test(t)}function p(t){if(l(t))return t;const e=t.match(i),n=e?e[0]:"",s=o(t);return r.test(s)?t:s+".html"+n}function h(t,e){const n=decodeURIComponent(t.hash),s=function(t){const e=t.match(i);if(e)return e[0]}(e);if(s&&n!==s)return!1;return o(t.path)===o(e)}function d(t,e,n){if(l(e))return{type:"external",path:e};n&&(e=function(t,e,n){const i=t.charAt(0);if("/"===i)return t;if("?"===i||"#"===i)return e+t;const s=e.split("/");n&&s[s.length-1]||s.pop();const r=t.replace(/^\//,"").split("/");for(let t=0;tfunction t(e,n,i,s=1){if("string"==typeof e)return d(n,e,i);if(Array.isArray(e))return Object.assign(d(n,e[0],i),{title:e[1]});{const r=e.children||[];return 0===r.length&&e.path?Object.assign(d(n,e.path,i),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:r.map(e=>t(e,n,i,s+1)),collapsable:!1!==e.collapsable}}}(t,s,n)):[]}return[]}function b(t){const e=m(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:e.map(e=>({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}function m(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function g(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},240:function(t,e,n){},241:function(t,e,n){"use strict";n.r(e);var i=n(239),s={name:"NavLink",props:{item:{required:!0}},computed:{link(){return Object(i.b)(this.item.link)},exact(){return this.$site.locales?Object.keys(this.$site.locales).some(t=>t===this.link):"/"===this.link},isNonHttpURI(){return Object(i.g)(this.link)||Object(i.h)(this.link)},isBlankTarget(){return"_blank"===this.target},isInternal(){return!Object(i.f)(this.link)&&!this.isBlankTarget},target(){return this.isNonHttpURI?null:this.item.target?this.item.target:Object(i.f)(this.link)?"_blank":""},rel(){return this.isNonHttpURI||!1===this.item.rel?null:this.item.rel?this.item.rel:this.isBlankTarget?"noopener noreferrer":null}},methods:{focusoutAction(){this.$emit("focusout")}}},r=n(14),a=Object(r.a)(s,(function(){var t=this,e=t._self._c;return t.isInternal?e("RouterLink",{staticClass:"nav-link",attrs:{to:t.link,exact:t.exact},nativeOn:{focusout:function(e){return t.focusoutAction.apply(null,arguments)}}},[t._v("\n "+t._s(t.item.text)+"\n")]):e("a",{staticClass:"nav-link external",attrs:{href:t.link,target:t.target,rel:t.rel},on:{focusout:t.focusoutAction}},[t._v("\n "+t._s(t.item.text)+"\n "),t.isBlankTarget?e("OutboundLink"):t._e()],1)}),[],!1,null,null,null);e.default=a.exports},242:function(t,e,n){"use strict";n.r(e);var i={name:"DropdownTransition",methods:{setHeight(t){t.style.height=t.scrollHeight+"px"},unsetHeight(t){t.style.height=""}}},s=(n(243),n(14)),r=Object(s.a)(i,(function(){return(0,this._self._c)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.default=r.exports},243:function(t,e,n){"use strict";n(240)},244:function(t,e,n){},245:function(t,e,n){},247:function(t,e,n){},250:function(t,e,n){},251:function(t,e,n){"use strict";n(244)},252:function(t,e,n){"use strict";n(245)},253:function(t,e,n){"use strict";n.r(e);var i=n(266),s=n(255),r=n(239);function a(t,e){if("group"===e.type){const n=e.path&&Object(r.e)(t,e.path),i=e.children.some(e=>"group"===e.type?a(t,e):"page"===e.type&&Object(r.e)(t,e.path));return n||i}return!1}var o={name:"SidebarLinks",components:{SidebarGroup:i.default,SidebarLink:s.default},props:["items","depth","sidebarDepth","initialOpenGroupIndex"],data(){return{openGroupIndex:this.initialOpenGroupIndex||0}},watch:{$route(){this.refreshIndex()}},created(){this.refreshIndex()},methods:{refreshIndex(){const t=function(t,e){for(let n=0;n-1&&(this.openGroupIndex=t)},toggleGroup(t){this.openGroupIndex=t===this.openGroupIndex?-1:t},isActive(t){return Object(r.e)(this.$route,t.regularPath)}}},l=n(14),u=Object(l.a)(o,(function(){var t=this,e=t._self._c;return t.items.length?e("ul",{staticClass:"sidebar-links"},t._l(t.items,(function(n,i){return e("li",{key:i},["group"===n.type?e("SidebarGroup",{attrs:{item:n,open:i===t.openGroupIndex,collapsable:n.collapsable||n.collapsible,depth:t.depth},on:{toggle:function(e){return t.toggleGroup(i)}}}):e("SidebarLink",{attrs:{"sidebar-depth":t.sidebarDepth,item:n}})],1)})),0):t._e()}),[],!1,null,null,null);e.default=u.exports},254:function(t,e,n){"use strict";n.r(e);var i=n(241),s=n(242),r=n(91),a=n.n(r),o={name:"DropdownLink",components:{NavLink:i.default,DropdownTransition:s.default},props:{item:{required:!0}},data:()=>({open:!1}),computed:{dropdownAriaLabel(){return this.item.ariaLabel||this.item.text}},watch:{$route(){this.open=!1}},methods:{setOpen(t){this.open=t},isLastItemOfArray:(t,e)=>a()(e)===t,handleDropdown(){0===event.detail&&this.setOpen(!this.open)}}},l=(n(251),n(14)),u=Object(l.a)(o,(function(){var t=this,e=t._self._c;return e("div",{staticClass:"dropdown-wrapper",class:{open:t.open}},[e("button",{staticClass:"dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:t.handleDropdown}},[e("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),e("span",{staticClass:"arrow down"})]),t._v(" "),e("button",{staticClass:"mobile-dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:function(e){return t.setOpen(!t.open)}}},[e("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),e("span",{staticClass:"arrow",class:t.open?"down":"right"})]),t._v(" "),e("DropdownTransition",[e("ul",{directives:[{name:"show",rawName:"v-show",value:t.open,expression:"open"}],staticClass:"nav-dropdown"},t._l(t.item.items,(function(n,i){return e("li",{key:n.link||i,staticClass:"dropdown-item"},["links"===n.type?e("h4",[t._v("\n "+t._s(n.text)+"\n ")]):t._e(),t._v(" "),"links"===n.type?e("ul",{staticClass:"dropdown-subitem-wrapper"},t._l(n.items,(function(i){return e("li",{key:i.link,staticClass:"dropdown-subitem"},[e("NavLink",{attrs:{item:i},on:{focusout:function(e){t.isLastItemOfArray(i,n.items)&&t.isLastItemOfArray(n,t.item.items)&&t.setOpen(!1)}}})],1)})),0):e("NavLink",{attrs:{item:n},on:{focusout:function(e){t.isLastItemOfArray(n,t.item.items)&&t.setOpen(!1)}}})],1)})),0)])],1)}),[],!1,null,null,null);e.default=u.exports},255:function(t,e,n){"use strict";n.r(e);var i=n(239);function s(t,e,n,i,s){const r={props:{to:e,activeClass:"",exactActiveClass:""},class:{active:i,"sidebar-link":!0}};return s>2&&(r.style={"padding-left":s+"rem"}),t("RouterLink",r,n)}function r(t,e,n,a,o,l=1){return!e||l>o?null:t("ul",{class:"sidebar-sub-headers"},e.map(e=>{const u=Object(i.e)(a,n+"#"+e.slug);return t("li",{class:"sidebar-sub-header"},[s(t,n+"#"+e.slug,e.title,u,e.level-1),r(t,e.children,n,a,o,l+1)])}))}var a={functional:!0,props:["item","sidebarDepth"],render(t,{parent:{$page:e,$site:n,$route:a,$themeConfig:o,$themeLocaleConfig:l},props:{item:u,sidebarDepth:c}}){const p=Object(i.e)(a,u.path),h="auto"===u.type?p||u.children.some(t=>Object(i.e)(a,u.basePath+"#"+t.slug)):p,d="external"===u.type?function(t,e,n){return t("a",{attrs:{href:e,target:"_blank",rel:"noopener noreferrer"},class:{"sidebar-link":!0}},[n,t("OutboundLink")])}(t,u.path,u.title||u.path):s(t,u.path,u.title||u.path,h),f=[e.frontmatter.sidebarDepth,c,l.sidebarDepth,o.sidebarDepth,1].find(t=>void 0!==t),b=l.displayAllHeaders||o.displayAllHeaders;if("auto"===u.type)return[d,r(t,u.children,u.basePath,a,f)];if((h||b)&&u.headers&&!i.d.test(u.path)){return[d,r(t,Object(i.c)(u.headers),u.path,a,f)]}return d}},o=(n(252),n(14)),l=Object(o.a)(a,void 0,void 0,!1,null,null,null);e.default=l.exports},257:function(t,e,n){"use strict";n(247)},263:function(t,e,n){"use strict";n(250)},264:function(t,e,n){},265:function(t,e,n){"use strict";n.r(e);var i=n(254),s=n(239),r={name:"NavLinks",components:{NavLink:n(241).default,DropdownLink:i.default},computed:{userNav(){return this.$themeLocaleConfig.nav||this.$site.themeConfig.nav||[]},nav(){const{locales:t}=this.$site;if(t&&Object.keys(t).length>1){const e=this.$page.path,n=this.$router.options.routes,i=this.$site.themeConfig.locales||{},s={text:this.$themeLocaleConfig.selectText||"Languages",ariaLabel:this.$themeLocaleConfig.ariaLabel||"Select language",items:Object.keys(t).map(s=>{const r=t[s],a=i[s]&&i[s].label||r.lang;let o;return r.lang===this.$lang?o=e:(o=e.replace(this.$localeConfig.path,s),n.some(t=>t.path===o)||(o=s)),{text:a,link:o}})};return[...this.userNav,s]}return this.userNav},userLinks(){return(this.nav||[]).map(t=>Object.assign(Object(s.j)(t),{items:(t.items||[]).map(s.j)}))},repoLink(){const{repo:t}=this.$site.themeConfig;return t?/^https?:/.test(t)?t:"https://github.com/"+t:null},repoLabel(){if(!this.repoLink)return;if(this.$site.themeConfig.repoLabel)return this.$site.themeConfig.repoLabel;const t=this.repoLink.match(/^https?:\/\/[^/]+/)[0],e=["GitHub","GitLab","Bitbucket"];for(let n=0;nfunction t(e,n,r,i=1){if("string"==typeof e)return d(n,e,r);if(Array.isArray(e))return Object.assign(d(n,e[0],r),{title:e[1]});{const a=e.children||[];return 0===a.length&&e.path?Object.assign(d(n,e.path,r),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:a.map(e=>t(e,n,r,i+1)),collapsable:!1!==e.collapsable}}}(t,i,n)):[]}return[]}function g(t){const e=v(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:e.map(e=>({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}function v(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function m(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},246:function(t,e){t.exports=function(t){return null==t}},248:function(t,e,n){},249:function(t,e,n){},259:function(t,e,n){"use strict";n(248)},260:function(t,e,n){var r=n(11),i=n(4),a=n(10);t.exports=function(t){return"string"==typeof t||!i(t)&&a(t)&&"[object String]"==r(t)}},261:function(t,e,n){"use strict";n(249)},262:function(t,e,n){},267:function(t,e,n){"use strict";n.r(e);var r=n(246),i=n.n(r),a=n(239),s={name:"PageEdit",computed:{lastUpdated(){return this.$page.lastUpdated},lastUpdatedText(){return"string"==typeof this.$themeLocaleConfig.lastUpdated?this.$themeLocaleConfig.lastUpdated:"string"==typeof this.$site.themeConfig.lastUpdated?this.$site.themeConfig.lastUpdated:"Last Updated"},editLink(){const t=i()(this.$page.frontmatter.editLink)?this.$site.themeConfig.editLinks:this.$page.frontmatter.editLink,{repo:e,docsDir:n="",docsBranch:r="master",docsRepo:a=e}=this.$site.themeConfig;return t&&a&&this.$page.relativePath?this.createEditLink(e,a,n,r,this.$page.relativePath):null},editLinkText(){return this.$themeLocaleConfig.editLinkText||this.$site.themeConfig.editLinkText||"Edit this page"}},methods:{createEditLink(t,e,n,r,i){if(/bitbucket.org/.test(e)){return e.replace(a.a,"")+"/src"+`/${r}/`+(n?n.replace(a.a,"")+"/":"")+i+`?mode=edit&spa=0&at=${r}&fileviewer=file-view-default`}if(/gitlab.com/.test(e)){return e.replace(a.a,"")+"/-/edit"+`/${r}/`+(n?n.replace(a.a,"")+"/":"")+i}return(a.i.test(e)?e:"https://github.com/"+e).replace(a.a,"")+"/edit"+`/${r}/`+(n?n.replace(a.a,"")+"/":"")+i}}},o=(n(259),n(14)),u=Object(o.a)(s,(function(){var t=this,e=t._self._c;return e("footer",{staticClass:"page-edit"},[t.editLink?e("div",{staticClass:"edit-link"},[e("a",{attrs:{href:t.editLink,target:"_blank",rel:"noopener noreferrer"}},[t._v(t._s(t.editLinkText))]),t._v(" "),e("OutboundLink")],1):t._e(),t._v(" "),t.lastUpdated?e("div",{staticClass:"last-updated"},[e("span",{staticClass:"prefix"},[t._v(t._s(t.lastUpdatedText)+":")]),t._v(" "),e("span",{staticClass:"time"},[t._v(t._s(t.lastUpdated))])]):t._e()])}),[],!1,null,null,null);e.default=u.exports},268:function(t,e,n){"use strict";n.r(e);n(90);var r=n(239),i=n(260),a=n.n(i),s=n(246),o=n.n(s),u={name:"PageNav",props:["sidebarItems"],computed:{prev(){return l(c.PREV,this)},next(){return l(c.NEXT,this)}}};const c={NEXT:{resolveLink:function(t,e){return p(t,e,1)},getThemeLinkConfig:({nextLinks:t})=>t,getPageLinkConfig:({frontmatter:t})=>t.next},PREV:{resolveLink:function(t,e){return p(t,e,-1)},getThemeLinkConfig:({prevLinks:t})=>t,getPageLinkConfig:({frontmatter:t})=>t.prev}};function l(t,{$themeConfig:e,$page:n,$route:i,$site:s,sidebarItems:u}){const{resolveLink:c,getThemeLinkConfig:l,getPageLinkConfig:p}=t,f=l(e),d=p(n),h=o()(d)?f:d;return!1===h?void 0:a()(h)?Object(r.k)(s.pages,h,i.path):c(n,u)}function p(t,e,n){const r=[];!function t(e,n){for(let r=0,i=e.length;rfunction t(e,n,i,r=1){if("string"==typeof e)return f(n,e,i);if(Array.isArray(e))return Object.assign(f(n,e[0],i),{title:e[1]});{const s=e.children||[];return 0===s.length&&e.path?Object.assign(f(n,e.path,i),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:s.map(e=>t(e,n,i,r+1)),collapsable:!1!==e.collapsable}}}(t,r,n)):[]}return[]}function m(t){const e=b(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:e.map(e=>({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}function b(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function g(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},240:function(t,e,n){},241:function(t,e,n){"use strict";n.r(e);var i=n(239),r={name:"NavLink",props:{item:{required:!0}},computed:{link(){return Object(i.b)(this.item.link)},exact(){return this.$site.locales?Object.keys(this.$site.locales).some(t=>t===this.link):"/"===this.link},isNonHttpURI(){return Object(i.g)(this.link)||Object(i.h)(this.link)},isBlankTarget(){return"_blank"===this.target},isInternal(){return!Object(i.f)(this.link)&&!this.isBlankTarget},target(){return this.isNonHttpURI?null:this.item.target?this.item.target:Object(i.f)(this.link)?"_blank":""},rel(){return this.isNonHttpURI||!1===this.item.rel?null:this.item.rel?this.item.rel:this.isBlankTarget?"noopener noreferrer":null}},methods:{focusoutAction(){this.$emit("focusout")}}},s=n(14),a=Object(s.a)(r,(function(){var t=this,e=t._self._c;return t.isInternal?e("RouterLink",{staticClass:"nav-link",attrs:{to:t.link,exact:t.exact},nativeOn:{focusout:function(e){return t.focusoutAction.apply(null,arguments)}}},[t._v("\n "+t._s(t.item.text)+"\n")]):e("a",{staticClass:"nav-link external",attrs:{href:t.link,target:t.target,rel:t.rel},on:{focusout:t.focusoutAction}},[t._v("\n "+t._s(t.item.text)+"\n "),t.isBlankTarget?e("OutboundLink"):t._e()],1)}),[],!1,null,null,null);e.default=a.exports},242:function(t,e,n){"use strict";n.r(e);var i={name:"DropdownTransition",methods:{setHeight(t){t.style.height=t.scrollHeight+"px"},unsetHeight(t){t.style.height=""}}},r=(n(243),n(14)),s=Object(r.a)(i,(function(){return(0,this._self._c)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.default=s.exports},243:function(t,e,n){"use strict";n(240)},244:function(t,e,n){},247:function(t,e,n){},251:function(t,e,n){"use strict";n(244)},254:function(t,e,n){"use strict";n.r(e);var i=n(241),r=n(242),s=n(91),a=n.n(s),o={name:"DropdownLink",components:{NavLink:i.default,DropdownTransition:r.default},props:{item:{required:!0}},data:()=>({open:!1}),computed:{dropdownAriaLabel(){return this.item.ariaLabel||this.item.text}},watch:{$route(){this.open=!1}},methods:{setOpen(t){this.open=t},isLastItemOfArray:(t,e)=>a()(e)===t,handleDropdown(){0===event.detail&&this.setOpen(!this.open)}}},l=(n(251),n(14)),u=Object(l.a)(o,(function(){var t=this,e=t._self._c;return e("div",{staticClass:"dropdown-wrapper",class:{open:t.open}},[e("button",{staticClass:"dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:t.handleDropdown}},[e("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),e("span",{staticClass:"arrow down"})]),t._v(" "),e("button",{staticClass:"mobile-dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:function(e){return t.setOpen(!t.open)}}},[e("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),e("span",{staticClass:"arrow",class:t.open?"down":"right"})]),t._v(" "),e("DropdownTransition",[e("ul",{directives:[{name:"show",rawName:"v-show",value:t.open,expression:"open"}],staticClass:"nav-dropdown"},t._l(t.item.items,(function(n,i){return e("li",{key:n.link||i,staticClass:"dropdown-item"},["links"===n.type?e("h4",[t._v("\n "+t._s(n.text)+"\n ")]):t._e(),t._v(" "),"links"===n.type?e("ul",{staticClass:"dropdown-subitem-wrapper"},t._l(n.items,(function(i){return e("li",{key:i.link,staticClass:"dropdown-subitem"},[e("NavLink",{attrs:{item:i},on:{focusout:function(e){t.isLastItemOfArray(i,n.items)&&t.isLastItemOfArray(n,t.item.items)&&t.setOpen(!1)}}})],1)})),0):e("NavLink",{attrs:{item:n},on:{focusout:function(e){t.isLastItemOfArray(n,t.item.items)&&t.setOpen(!1)}}})],1)})),0)])],1)}),[],!1,null,null,null);e.default=u.exports},257:function(t,e,n){"use strict";n(247)},265:function(t,e,n){"use strict";n.r(e);var i=n(254),r=n(239),s={name:"NavLinks",components:{NavLink:n(241).default,DropdownLink:i.default},computed:{userNav(){return this.$themeLocaleConfig.nav||this.$site.themeConfig.nav||[]},nav(){const{locales:t}=this.$site;if(t&&Object.keys(t).length>1){const e=this.$page.path,n=this.$router.options.routes,i=this.$site.themeConfig.locales||{},r={text:this.$themeLocaleConfig.selectText||"Languages",ariaLabel:this.$themeLocaleConfig.ariaLabel||"Select language",items:Object.keys(t).map(r=>{const s=t[r],a=i[r]&&i[r].label||s.lang;let o;return s.lang===this.$lang?o=e:(o=e.replace(this.$localeConfig.path,r),n.some(t=>t.path===o)||(o=r)),{text:a,link:o}})};return[...this.userNav,r]}return this.userNav},userLinks(){return(this.nav||[]).map(t=>Object.assign(Object(r.j)(t),{items:(t.items||[]).map(r.j)}))},repoLink(){const{repo:t}=this.$site.themeConfig;return t?/^https?:/.test(t)?t:"https://github.com/"+t:null},repoLabel(){if(!this.repoLink)return;if(this.$site.themeConfig.repoLabel)return this.$site.themeConfig.repoLabel;const t=this.repoLink.match(/^https?:\/\/[^/]+/)[0],e=["GitHub","GitLab","Bitbucket"];for(let n=0;n({placeholder:void 0}),watch:{$lang(e){this.update(this.options,e)},options(e){this.update(e,this.$lang)}},mounted(){this.initialize(this.options,this.$lang),this.placeholder=this.$site.themeConfig.searchPlaceholder||""},methods:{initialize(e,t){Promise.all([Promise.all([a.e(0),a.e(9)]).then(a.t.bind(null,295,7)),Promise.all([a.e(0),a.e(9)]).then(a.t.bind(null,296,7))]).then(([a])=>{a=a.default;const{algoliaOptions:i={}}=e;a(Object.assign({},e,{inputSelector:"#algolia-search-input",algoliaOptions:{...i,facetFilters:["lang:"+t].concat(i.facetFilters||[])},handleSelected:(e,t,a)=>{const{pathname:i,hash:n}=new URL(a.url),r=i.replace(this.$site.base,"/"),s=decodeURIComponent(n);this.$router.push(`${r}${s}`)}}))})},update(e,t){this.$el.innerHTML='',this.initialize(e,t)}}},n=(a(288),a(14)),r=Object(n.a)(i,(function(){var e=this._self._c;return e("form",{staticClass:"algolia-search-wrapper search-box",attrs:{id:"search-form",role:"search"}},[e("input",{staticClass:"search-query",attrs:{id:"algolia-search-input",placeholder:this.placeholder}})])}),[],!1,null,null,null);t.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/14.3e65117c.js b/assets/js/14.3e65117c.js new file mode 100644 index 0000000..95ae75c --- /dev/null +++ b/assets/js/14.3e65117c.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[14,19,21],{239:function(t,e,n){"use strict";n.d(e,"d",(function(){return i})),n.d(e,"a",(function(){return s})),n.d(e,"i",(function(){return o})),n.d(e,"f",(function(){return l})),n.d(e,"g",(function(){return u})),n.d(e,"h",(function(){return c})),n.d(e,"b",(function(){return p})),n.d(e,"e",(function(){return f})),n.d(e,"k",(function(){return h})),n.d(e,"l",(function(){return d})),n.d(e,"c",(function(){return b})),n.d(e,"j",(function(){return g}));n(90);const i=/#.*$/,r=/\.(md|html)$/,s=/\/$/,o=/^[a-z]+:/i;function a(t){return decodeURI(t).replace(i,"").replace(r,"")}function l(t){return o.test(t)}function u(t){return/^mailto:/.test(t)}function c(t){return/^tel:/.test(t)}function p(t){if(l(t))return t;const e=t.match(i),n=e?e[0]:"",r=a(t);return s.test(r)?t:r+".html"+n}function f(t,e){const n=decodeURIComponent(t.hash),r=function(t){const e=t.match(i);if(e)return e[0]}(e);if(r&&n!==r)return!1;return a(t.path)===a(e)}function h(t,e,n){if(l(e))return{type:"external",path:e};n&&(e=function(t,e,n){const i=t.charAt(0);if("/"===i)return t;if("?"===i||"#"===i)return e+t;const r=e.split("/");n&&r[r.length-1]||r.pop();const s=t.replace(/^\//,"").split("/");for(let t=0;tfunction t(e,n,i,r=1){if("string"==typeof e)return h(n,e,i);if(Array.isArray(e))return Object.assign(h(n,e[0],i),{title:e[1]});{const s=e.children||[];return 0===s.length&&e.path?Object.assign(h(n,e.path,i),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:s.map(e=>t(e,n,i,r+1)),collapsable:!1!==e.collapsable}}}(t,r,n)):[]}return[]}function m(t){const e=b(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:e.map(e=>({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}function b(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function g(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},240:function(t,e,n){},241:function(t,e,n){"use strict";n.r(e);var i=n(239),r={name:"NavLink",props:{item:{required:!0}},computed:{link(){return Object(i.b)(this.item.link)},exact(){return this.$site.locales?Object.keys(this.$site.locales).some(t=>t===this.link):"/"===this.link},isNonHttpURI(){return Object(i.g)(this.link)||Object(i.h)(this.link)},isBlankTarget(){return"_blank"===this.target},isInternal(){return!Object(i.f)(this.link)&&!this.isBlankTarget},target(){return this.isNonHttpURI?null:this.item.target?this.item.target:Object(i.f)(this.link)?"_blank":""},rel(){return this.isNonHttpURI||!1===this.item.rel?null:this.item.rel?this.item.rel:this.isBlankTarget?"noopener noreferrer":null}},methods:{focusoutAction(){this.$emit("focusout")}}},s=n(14),o=Object(s.a)(r,(function(){var t=this,e=t._self._c;return t.isInternal?e("RouterLink",{staticClass:"nav-link",attrs:{to:t.link,exact:t.exact},nativeOn:{focusout:function(e){return t.focusoutAction.apply(null,arguments)}}},[t._v("\n "+t._s(t.item.text)+"\n")]):e("a",{staticClass:"nav-link external",attrs:{href:t.link,target:t.target,rel:t.rel},on:{focusout:t.focusoutAction}},[t._v("\n "+t._s(t.item.text)+"\n "),t.isBlankTarget?e("OutboundLink"):t._e()],1)}),[],!1,null,null,null);e.default=o.exports},242:function(t,e,n){"use strict";n.r(e);var i={name:"DropdownTransition",methods:{setHeight(t){t.style.height=t.scrollHeight+"px"},unsetHeight(t){t.style.height=""}}},r=(n(243),n(14)),s=Object(r.a)(i,(function(){return(0,this._self._c)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.default=s.exports},243:function(t,e,n){"use strict";n(240)},244:function(t,e,n){},251:function(t,e,n){"use strict";n(244)},254:function(t,e,n){"use strict";n.r(e);var i=n(241),r=n(242),s=n(91),o=n.n(s),a={name:"DropdownLink",components:{NavLink:i.default,DropdownTransition:r.default},props:{item:{required:!0}},data:()=>({open:!1}),computed:{dropdownAriaLabel(){return this.item.ariaLabel||this.item.text}},watch:{$route(){this.open=!1}},methods:{setOpen(t){this.open=t},isLastItemOfArray:(t,e)=>o()(e)===t,handleDropdown(){0===event.detail&&this.setOpen(!this.open)}}},l=(n(251),n(14)),u=Object(l.a)(a,(function(){var t=this,e=t._self._c;return e("div",{staticClass:"dropdown-wrapper",class:{open:t.open}},[e("button",{staticClass:"dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:t.handleDropdown}},[e("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),e("span",{staticClass:"arrow down"})]),t._v(" "),e("button",{staticClass:"mobile-dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:function(e){return t.setOpen(!t.open)}}},[e("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),e("span",{staticClass:"arrow",class:t.open?"down":"right"})]),t._v(" "),e("DropdownTransition",[e("ul",{directives:[{name:"show",rawName:"v-show",value:t.open,expression:"open"}],staticClass:"nav-dropdown"},t._l(t.item.items,(function(n,i){return e("li",{key:n.link||i,staticClass:"dropdown-item"},["links"===n.type?e("h4",[t._v("\n "+t._s(n.text)+"\n ")]):t._e(),t._v(" "),"links"===n.type?e("ul",{staticClass:"dropdown-subitem-wrapper"},t._l(n.items,(function(i){return e("li",{key:i.link,staticClass:"dropdown-subitem"},[e("NavLink",{attrs:{item:i},on:{focusout:function(e){t.isLastItemOfArray(i,n.items)&&t.isLastItemOfArray(n,t.item.items)&&t.setOpen(!1)}}})],1)})),0):e("NavLink",{attrs:{item:n},on:{focusout:function(e){t.isLastItemOfArray(n,t.item.items)&&t.setOpen(!1)}}})],1)})),0)])],1)}),[],!1,null,null,null);e.default=u.exports}}]); \ No newline at end of file diff --git a/assets/js/15.2d87bde7.js b/assets/js/15.2d87bde7.js new file mode 100644 index 0000000..77d5618 --- /dev/null +++ b/assets/js/15.2d87bde7.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{239:function(t,n,e){"use strict";e.d(n,"d",(function(){return r})),e.d(n,"a",(function(){return o})),e.d(n,"i",(function(){return a})),e.d(n,"f",(function(){return u})),e.d(n,"g",(function(){return c})),e.d(n,"h",(function(){return p})),e.d(n,"b",(function(){return l})),e.d(n,"e",(function(){return f})),e.d(n,"k",(function(){return h})),e.d(n,"l",(function(){return d})),e.d(n,"c",(function(){return v})),e.d(n,"j",(function(){return b}));e(90);const r=/#.*$/,i=/\.(md|html)$/,o=/\/$/,a=/^[a-z]+:/i;function s(t){return decodeURI(t).replace(r,"").replace(i,"")}function u(t){return a.test(t)}function c(t){return/^mailto:/.test(t)}function p(t){return/^tel:/.test(t)}function l(t){if(u(t))return t;const n=t.match(r),e=n?n[0]:"",i=s(t);return o.test(i)?t:i+".html"+e}function f(t,n){const e=decodeURIComponent(t.hash),i=function(t){const n=t.match(r);if(n)return n[0]}(n);if(i&&e!==i)return!1;return s(t.path)===s(n)}function h(t,n,e){if(u(n))return{type:"external",path:n};e&&(n=function(t,n,e){const r=t.charAt(0);if("/"===r)return t;if("?"===r||"#"===r)return n+t;const i=n.split("/");e&&i[i.length-1]||i.pop();const o=t.replace(/^\//,"").split("/");for(let t=0;tfunction t(n,e,r,i=1){if("string"==typeof n)return h(e,n,r);if(Array.isArray(n))return Object.assign(h(e,n[0],r),{title:n[1]});{const o=n.children||[];return 0===o.length&&n.path?Object.assign(h(e,n.path,r),{title:n.title}):{type:"group",path:n.path,title:n.title,sidebarDepth:n.sidebarDepth,initialOpenGroupIndex:n.initialOpenGroupIndex,children:o.map(n=>t(n,e,r,i+1)),collapsable:!1!==n.collapsable}}}(t,i,e)):[]}return[]}function g(t){const n=v(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:n.map(n=>({type:"auto",title:n.title,basePath:t.path,path:t.path+"#"+n.slug,children:n.children||[]}))}]}function v(t){let n;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?n=t:n&&(n.children||(n.children=[])).push(t)}),t.filter(t=>2===t.level)}function b(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},246:function(t,n){t.exports=function(t){return null==t}},249:function(t,n,e){},260:function(t,n,e){var r=e(11),i=e(4),o=e(10);t.exports=function(t){return"string"==typeof t||!i(t)&&o(t)&&"[object String]"==r(t)}},261:function(t,n,e){"use strict";e(249)},268:function(t,n,e){"use strict";e.r(n);e(90);var r=e(239),i=e(260),o=e.n(i),a=e(246),s=e.n(a),u={name:"PageNav",props:["sidebarItems"],computed:{prev(){return p(c.PREV,this)},next(){return p(c.NEXT,this)}}};const c={NEXT:{resolveLink:function(t,n){return l(t,n,1)},getThemeLinkConfig:({nextLinks:t})=>t,getPageLinkConfig:({frontmatter:t})=>t.next},PREV:{resolveLink:function(t,n){return l(t,n,-1)},getThemeLinkConfig:({prevLinks:t})=>t,getPageLinkConfig:({frontmatter:t})=>t.prev}};function p(t,{$themeConfig:n,$page:e,$route:i,$site:a,sidebarItems:u}){const{resolveLink:c,getThemeLinkConfig:p,getPageLinkConfig:l}=t,f=p(n),h=l(e),d=s()(h)?f:h;return!1===d?void 0:o()(d)?Object(r.k)(a.pages,d,i.path):c(e,u)}function l(t,n,e){const r=[];!function t(n,e){for(let r=0,i=n.length;rfunction t(e,n,i,r=1){if("string"==typeof e)return d(n,e,i);if(Array.isArray(e))return Object.assign(d(n,e[0],i),{title:e[1]});{const a=e.children||[];return 0===a.length&&e.path?Object.assign(d(n,e.path,i),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:a.map(e=>t(e,n,i,r+1)),collapsable:!1!==e.collapsable}}}(t,r,n)):[]}return[]}function m(t){const e=g(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:e.map(e=>({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}function g(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function b(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},241:function(t,e,n){"use strict";n.r(e);var i=n(239),r={name:"NavLink",props:{item:{required:!0}},computed:{link(){return Object(i.b)(this.item.link)},exact(){return this.$site.locales?Object.keys(this.$site.locales).some(t=>t===this.link):"/"===this.link},isNonHttpURI(){return Object(i.g)(this.link)||Object(i.h)(this.link)},isBlankTarget(){return"_blank"===this.target},isInternal(){return!Object(i.f)(this.link)&&!this.isBlankTarget},target(){return this.isNonHttpURI?null:this.item.target?this.item.target:Object(i.f)(this.link)?"_blank":""},rel(){return this.isNonHttpURI||!1===this.item.rel?null:this.item.rel?this.item.rel:this.isBlankTarget?"noopener noreferrer":null}},methods:{focusoutAction(){this.$emit("focusout")}}},a=n(14),s=Object(a.a)(r,(function(){var t=this,e=t._self._c;return t.isInternal?e("RouterLink",{staticClass:"nav-link",attrs:{to:t.link,exact:t.exact},nativeOn:{focusout:function(e){return t.focusoutAction.apply(null,arguments)}}},[t._v("\n "+t._s(t.item.text)+"\n")]):e("a",{staticClass:"nav-link external",attrs:{href:t.link,target:t.target,rel:t.rel},on:{focusout:t.focusoutAction}},[t._v("\n "+t._s(t.item.text)+"\n "),t.isBlankTarget?e("OutboundLink"):t._e()],1)}),[],!1,null,null,null);e.default=s.exports},256:function(t,e,n){},269:function(t,e,n){"use strict";n(256)},280:function(t,e,n){"use strict";n.r(e);var i={name:"Home",components:{NavLink:n(241).default},computed:{data(){return this.$page.frontmatter},actionLink(){return{link:this.data.actionLink,text:this.data.actionText}}}},r=(n(269),n(14)),a=Object(r.a)(i,(function(){var t=this,e=t._self._c;return e("main",{staticClass:"home",attrs:{"aria-labelledby":null!==t.data.heroText?"main-title":null}},[e("header",{staticClass:"hero"},[t.data.heroImage?e("img",{attrs:{src:t.$withBase(t.data.heroImage),alt:t.data.heroAlt||"hero"}}):t._e(),t._v(" "),null!==t.data.heroText?e("h1",{attrs:{id:"main-title"}},[t._v("\n "+t._s(t.data.heroText||t.$title||"Hello")+"\n ")]):t._e(),t._v(" "),null!==t.data.tagline?e("p",{staticClass:"description"},[t._v("\n "+t._s(t.data.tagline||t.$description||"Welcome to your VuePress site")+"\n ")]):t._e(),t._v(" "),t.data.actionText&&t.data.actionLink?e("p",{staticClass:"action"},[e("NavLink",{staticClass:"action-button",attrs:{item:t.actionLink}})],1):t._e()]),t._v(" "),t.data.features&&t.data.features.length?e("div",{staticClass:"features"},t._l(t.data.features,(function(n,i){return e("div",{key:i,staticClass:"feature"},[e("h2",[t._v(t._s(n.title))]),t._v(" "),e("p",[t._v(t._s(n.details))])])})),0):t._e(),t._v(" "),e("Content",{staticClass:"theme-default-content custom"}),t._v(" "),t.data.footer?e("div",{staticClass:"footer"},[t._v("\n "+t._s(t.data.footer)+"\n ")]):e("Content",{staticClass:"footer",attrs:{"slot-key":"footer"}})],1)}),[],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/17.3dbcb51f.js b/assets/js/17.3dbcb51f.js new file mode 100644 index 0000000..affb29d --- /dev/null +++ b/assets/js/17.3dbcb51f.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{239:function(t,e,n){"use strict";n.d(e,"d",(function(){return i})),n.d(e,"a",(function(){return a})),n.d(e,"i",(function(){return s})),n.d(e,"f",(function(){return c})),n.d(e,"g",(function(){return u})),n.d(e,"h",(function(){return l})),n.d(e,"b",(function(){return d})),n.d(e,"e",(function(){return p})),n.d(e,"k",(function(){return f})),n.d(e,"l",(function(){return h})),n.d(e,"c",(function(){return m})),n.d(e,"j",(function(){return b}));n(90);const i=/#.*$/,r=/\.(md|html)$/,a=/\/$/,s=/^[a-z]+:/i;function o(t){return decodeURI(t).replace(i,"").replace(r,"")}function c(t){return s.test(t)}function u(t){return/^mailto:/.test(t)}function l(t){return/^tel:/.test(t)}function d(t){if(c(t))return t;const e=t.match(i),n=e?e[0]:"",r=o(t);return a.test(r)?t:r+".html"+n}function p(t,e){const n=decodeURIComponent(t.hash),r=function(t){const e=t.match(i);if(e)return e[0]}(e);if(r&&n!==r)return!1;return o(t.path)===o(e)}function f(t,e,n){if(c(e))return{type:"external",path:e};n&&(e=function(t,e,n){const i=t.charAt(0);if("/"===i)return t;if("?"===i||"#"===i)return e+t;const r=e.split("/");n&&r[r.length-1]||r.pop();const a=t.replace(/^\//,"").split("/");for(let t=0;tfunction t(e,n,i,r=1){if("string"==typeof e)return f(n,e,i);if(Array.isArray(e))return Object.assign(f(n,e[0],i),{title:e[1]});{const a=e.children||[];return 0===a.length&&e.path?Object.assign(f(n,e.path,i),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:a.map(e=>t(e,n,i,r+1)),collapsable:!1!==e.collapsable}}}(t,r,n)):[]}return[]}function g(t){const e=m(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:e.map(e=>({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}function m(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function b(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},246:function(t,e){t.exports=function(t){return null==t}},248:function(t,e,n){},259:function(t,e,n){"use strict";n(248)},267:function(t,e,n){"use strict";n.r(e);var i=n(246),r=n.n(i),a=n(239),s={name:"PageEdit",computed:{lastUpdated(){return this.$page.lastUpdated},lastUpdatedText(){return"string"==typeof this.$themeLocaleConfig.lastUpdated?this.$themeLocaleConfig.lastUpdated:"string"==typeof this.$site.themeConfig.lastUpdated?this.$site.themeConfig.lastUpdated:"Last Updated"},editLink(){const t=r()(this.$page.frontmatter.editLink)?this.$site.themeConfig.editLinks:this.$page.frontmatter.editLink,{repo:e,docsDir:n="",docsBranch:i="master",docsRepo:a=e}=this.$site.themeConfig;return t&&a&&this.$page.relativePath?this.createEditLink(e,a,n,i,this.$page.relativePath):null},editLinkText(){return this.$themeLocaleConfig.editLinkText||this.$site.themeConfig.editLinkText||"Edit this page"}},methods:{createEditLink(t,e,n,i,r){if(/bitbucket.org/.test(e)){return e.replace(a.a,"")+"/src"+`/${i}/`+(n?n.replace(a.a,"")+"/":"")+r+`?mode=edit&spa=0&at=${i}&fileviewer=file-view-default`}if(/gitlab.com/.test(e)){return e.replace(a.a,"")+"/-/edit"+`/${i}/`+(n?n.replace(a.a,"")+"/":"")+r}return(a.i.test(e)?e:"https://github.com/"+e).replace(a.a,"")+"/edit"+`/${i}/`+(n?n.replace(a.a,"")+"/":"")+r}}},o=(n(259),n(14)),c=Object(o.a)(s,(function(){var t=this,e=t._self._c;return e("footer",{staticClass:"page-edit"},[t.editLink?e("div",{staticClass:"edit-link"},[e("a",{attrs:{href:t.editLink,target:"_blank",rel:"noopener noreferrer"}},[t._v(t._s(t.editLinkText))]),t._v(" "),e("OutboundLink")],1):t._e(),t._v(" "),t.lastUpdated?e("div",{staticClass:"last-updated"},[e("span",{staticClass:"prefix"},[t._v(t._s(t.lastUpdatedText)+":")]),t._v(" "),e("span",{staticClass:"time"},[t._v(t._s(t.lastUpdated))])]):t._e()])}),[],!1,null,null,null);e.default=c.exports}}]); \ No newline at end of file diff --git a/assets/js/18.4c170722.js b/assets/js/18.4c170722.js new file mode 100644 index 0000000..07ed4be --- /dev/null +++ b/assets/js/18.4c170722.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[18],{239:function(t,e,n){"use strict";n.d(e,"d",(function(){return r})),n.d(e,"a",(function(){return a})),n.d(e,"i",(function(){return s})),n.d(e,"f",(function(){return o})),n.d(e,"g",(function(){return c})),n.d(e,"h",(function(){return l})),n.d(e,"b",(function(){return p})),n.d(e,"e",(function(){return f})),n.d(e,"k",(function(){return d})),n.d(e,"l",(function(){return h})),n.d(e,"c",(function(){return g})),n.d(e,"j",(function(){return m}));n(90);const r=/#.*$/,i=/\.(md|html)$/,a=/\/$/,s=/^[a-z]+:/i;function u(t){return decodeURI(t).replace(r,"").replace(i,"")}function o(t){return s.test(t)}function c(t){return/^mailto:/.test(t)}function l(t){return/^tel:/.test(t)}function p(t){if(o(t))return t;const e=t.match(r),n=e?e[0]:"",i=u(t);return a.test(i)?t:i+".html"+n}function f(t,e){const n=decodeURIComponent(t.hash),i=function(t){const e=t.match(r);if(e)return e[0]}(e);if(i&&n!==i)return!1;return u(t.path)===u(e)}function d(t,e,n){if(o(e))return{type:"external",path:e};n&&(e=function(t,e,n){const r=t.charAt(0);if("/"===r)return t;if("?"===r||"#"===r)return e+t;const i=e.split("/");n&&i[i.length-1]||i.pop();const a=t.replace(/^\//,"").split("/");for(let t=0;tfunction t(e,n,r,i=1){if("string"==typeof e)return d(n,e,r);if(Array.isArray(e))return Object.assign(d(n,e[0],r),{title:e[1]});{const a=e.children||[];return 0===a.length&&e.path?Object.assign(d(n,e.path,r),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:a.map(e=>t(e,n,r,i+1)),collapsable:!1!==e.collapsable}}}(t,i,n)):[]}return[]}function b(t){const e=g(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:e.map(e=>({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}function g(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function m(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},245:function(t,e,n){},252:function(t,e,n){"use strict";n(245)},255:function(t,e,n){"use strict";n.r(e);var r=n(239);function i(t,e,n,r,i){const a={props:{to:e,activeClass:"",exactActiveClass:""},class:{active:r,"sidebar-link":!0}};return i>2&&(a.style={"padding-left":i+"rem"}),t("RouterLink",a,n)}function a(t,e,n,s,u,o=1){return!e||o>u?null:t("ul",{class:"sidebar-sub-headers"},e.map(e=>{const c=Object(r.e)(s,n+"#"+e.slug);return t("li",{class:"sidebar-sub-header"},[i(t,n+"#"+e.slug,e.title,c,e.level-1),a(t,e.children,n,s,u,o+1)])}))}var s={functional:!0,props:["item","sidebarDepth"],render(t,{parent:{$page:e,$site:n,$route:s,$themeConfig:u,$themeLocaleConfig:o},props:{item:c,sidebarDepth:l}}){const p=Object(r.e)(s,c.path),f="auto"===c.type?p||c.children.some(t=>Object(r.e)(s,c.basePath+"#"+t.slug)):p,d="external"===c.type?function(t,e,n){return t("a",{attrs:{href:e,target:"_blank",rel:"noopener noreferrer"},class:{"sidebar-link":!0}},[n,t("OutboundLink")])}(t,c.path,c.title||c.path):i(t,c.path,c.title||c.path,f),h=[e.frontmatter.sidebarDepth,l,o.sidebarDepth,u.sidebarDepth,1].find(t=>void 0!==t),b=o.displayAllHeaders||u.displayAllHeaders;if("auto"===c.type)return[d,a(t,c.children,c.basePath,s,h)];if((f||b)&&c.headers&&!r.d.test(c.path)){return[d,a(t,Object(r.c)(c.headers),c.path,s,h)]}return d}},u=(n(252),n(14)),o=Object(u.a)(s,void 0,void 0,!1,null,null,null);e.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/19.d8afd0ae.js b/assets/js/19.d8afd0ae.js new file mode 100644 index 0000000..ffda412 --- /dev/null +++ b/assets/js/19.d8afd0ae.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[19],{240:function(t,e,n){},242:function(t,e,n){"use strict";n.r(e);var s={name:"DropdownTransition",methods:{setHeight(t){t.style.height=t.scrollHeight+"px"},unsetHeight(t){t.style.height=""}}},i=(n(243),n(14)),o=Object(i.a)(s,(function(){return(0,this._self._c)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.default=o.exports},243:function(t,e,n){"use strict";n(240)}}]); \ No newline at end of file diff --git a/assets/js/2.58244656.js b/assets/js/2.58244656.js new file mode 100644 index 0000000..519121c --- /dev/null +++ b/assets/js/2.58244656.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{245:function(t,e,a){},246:function(t,e){t.exports=function(t){return null==t}},248:function(t,e,a){},249:function(t,e,a){},250:function(t,e,a){},252:function(t,e,a){"use strict";a(245)},253:function(t,e,a){"use strict";a.r(e);var n=a(266),s=a(255),i=a(239);function r(t,e){if("group"===e.type){const a=e.path&&Object(i.e)(t,e.path),n=e.children.some(e=>"group"===e.type?r(t,e):"page"===e.type&&Object(i.e)(t,e.path));return a||n}return!1}var o={name:"SidebarLinks",components:{SidebarGroup:n.default,SidebarLink:s.default},props:["items","depth","sidebarDepth","initialOpenGroupIndex"],data(){return{openGroupIndex:this.initialOpenGroupIndex||0}},watch:{$route(){this.refreshIndex()}},created(){this.refreshIndex()},methods:{refreshIndex(){const t=function(t,e){for(let a=0;a-1&&(this.openGroupIndex=t)},toggleGroup(t){this.openGroupIndex=t===this.openGroupIndex?-1:t},isActive(t){return Object(i.e)(this.$route,t.regularPath)}}},l=a(14),u=Object(l.a)(o,(function(){var t=this,e=t._self._c;return t.items.length?e("ul",{staticClass:"sidebar-links"},t._l(t.items,(function(a,n){return e("li",{key:n},["group"===a.type?e("SidebarGroup",{attrs:{item:a,open:n===t.openGroupIndex,collapsable:a.collapsable||a.collapsible,depth:t.depth},on:{toggle:function(e){return t.toggleGroup(n)}}}):e("SidebarLink",{attrs:{"sidebar-depth":t.sidebarDepth,item:a}})],1)})),0):t._e()}),[],!1,null,null,null);e.default=u.exports},255:function(t,e,a){"use strict";a.r(e);var n=a(239);function s(t,e,a,n,s){const i={props:{to:e,activeClass:"",exactActiveClass:""},class:{active:n,"sidebar-link":!0}};return s>2&&(i.style={"padding-left":s+"rem"}),t("RouterLink",i,a)}function i(t,e,a,r,o,l=1){return!e||l>o?null:t("ul",{class:"sidebar-sub-headers"},e.map(e=>{const u=Object(n.e)(r,a+"#"+e.slug);return t("li",{class:"sidebar-sub-header"},[s(t,a+"#"+e.slug,e.title,u,e.level-1),i(t,e.children,a,r,o,l+1)])}))}var r={functional:!0,props:["item","sidebarDepth"],render(t,{parent:{$page:e,$site:a,$route:r,$themeConfig:o,$themeLocaleConfig:l},props:{item:u,sidebarDepth:c}}){const p=Object(n.e)(r,u.path),d="auto"===u.type?p||u.children.some(t=>Object(n.e)(r,u.basePath+"#"+t.slug)):p,h="external"===u.type?function(t,e,a){return t("a",{attrs:{href:e,target:"_blank",rel:"noopener noreferrer"},class:{"sidebar-link":!0}},[a,t("OutboundLink")])}(t,u.path,u.title||u.path):s(t,u.path,u.title||u.path,d),f=[e.frontmatter.sidebarDepth,c,l.sidebarDepth,o.sidebarDepth,1].find(t=>void 0!==t),b=l.displayAllHeaders||o.displayAllHeaders;if("auto"===u.type)return[h,i(t,u.children,u.basePath,r,f)];if((d||b)&&u.headers&&!n.d.test(u.path)){return[h,i(t,Object(n.c)(u.headers),u.path,r,f)]}return h}},o=(a(252),a(14)),l=Object(o.a)(r,void 0,void 0,!1,null,null,null);e.default=l.exports},256:function(t,e,a){},259:function(t,e,a){"use strict";a(248)},260:function(t,e,a){var n=a(11),s=a(4),i=a(10);t.exports=function(t){return"string"==typeof t||!s(t)&&i(t)&&"[object String]"==n(t)}},261:function(t,e,a){"use strict";a(249)},262:function(t,e,a){},263:function(t,e,a){"use strict";a(250)},264:function(t,e,a){},266:function(t,e,a){"use strict";a.r(e);var n=a(239),s={name:"SidebarGroup",components:{DropdownTransition:a(242).default},props:["item","open","collapsable","depth"],beforeCreate(){this.$options.components.SidebarLinks=a(253).default},methods:{isActive:n.e}},i=(a(263),a(14)),r=Object(i.a)(s,(function(){var t=this,e=t._self._c;return e("section",{staticClass:"sidebar-group",class:[{collapsable:t.collapsable,"is-sub-group":0!==t.depth},"depth-"+t.depth]},[t.item.path?e("RouterLink",{staticClass:"sidebar-heading clickable",class:{open:t.open,active:t.isActive(t.$route,t.item.path)},attrs:{to:t.item.path},nativeOn:{click:function(e){return t.$emit("toggle")}}},[e("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?e("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]):e("p",{staticClass:"sidebar-heading",class:{open:t.open},on:{click:function(e){return t.$emit("toggle")}}},[e("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?e("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]),t._v(" "),e("DropdownTransition",[t.open||!t.collapsable?e("SidebarLinks",{staticClass:"sidebar-group-items",attrs:{items:t.item.children,"sidebar-depth":t.item.sidebarDepth,"initial-open-group-index":t.item.initialOpenGroupIndex,depth:t.depth+1}}):t._e()],1)],1)}),[],!1,null,null,null);e.default=r.exports},267:function(t,e,a){"use strict";a.r(e);var n=a(246),s=a.n(n),i=a(239),r={name:"PageEdit",computed:{lastUpdated(){return this.$page.lastUpdated},lastUpdatedText(){return"string"==typeof this.$themeLocaleConfig.lastUpdated?this.$themeLocaleConfig.lastUpdated:"string"==typeof this.$site.themeConfig.lastUpdated?this.$site.themeConfig.lastUpdated:"Last Updated"},editLink(){const t=s()(this.$page.frontmatter.editLink)?this.$site.themeConfig.editLinks:this.$page.frontmatter.editLink,{repo:e,docsDir:a="",docsBranch:n="master",docsRepo:i=e}=this.$site.themeConfig;return t&&i&&this.$page.relativePath?this.createEditLink(e,i,a,n,this.$page.relativePath):null},editLinkText(){return this.$themeLocaleConfig.editLinkText||this.$site.themeConfig.editLinkText||"Edit this page"}},methods:{createEditLink(t,e,a,n,s){if(/bitbucket.org/.test(e)){return e.replace(i.a,"")+"/src"+`/${n}/`+(a?a.replace(i.a,"")+"/":"")+s+`?mode=edit&spa=0&at=${n}&fileviewer=file-view-default`}if(/gitlab.com/.test(e)){return e.replace(i.a,"")+"/-/edit"+`/${n}/`+(a?a.replace(i.a,"")+"/":"")+s}return(i.i.test(e)?e:"https://github.com/"+e).replace(i.a,"")+"/edit"+`/${n}/`+(a?a.replace(i.a,"")+"/":"")+s}}},o=(a(259),a(14)),l=Object(o.a)(r,(function(){var t=this,e=t._self._c;return e("footer",{staticClass:"page-edit"},[t.editLink?e("div",{staticClass:"edit-link"},[e("a",{attrs:{href:t.editLink,target:"_blank",rel:"noopener noreferrer"}},[t._v(t._s(t.editLinkText))]),t._v(" "),e("OutboundLink")],1):t._e(),t._v(" "),t.lastUpdated?e("div",{staticClass:"last-updated"},[e("span",{staticClass:"prefix"},[t._v(t._s(t.lastUpdatedText)+":")]),t._v(" "),e("span",{staticClass:"time"},[t._v(t._s(t.lastUpdated))])]):t._e()])}),[],!1,null,null,null);e.default=l.exports},268:function(t,e,a){"use strict";a.r(e);a(90);var n=a(239),s=a(260),i=a.n(s),r=a(246),o=a.n(r),l={name:"PageNav",props:["sidebarItems"],computed:{prev(){return c(u.PREV,this)},next(){return c(u.NEXT,this)}}};const u={NEXT:{resolveLink:function(t,e){return p(t,e,1)},getThemeLinkConfig:({nextLinks:t})=>t,getPageLinkConfig:({frontmatter:t})=>t.next},PREV:{resolveLink:function(t,e){return p(t,e,-1)},getThemeLinkConfig:({prevLinks:t})=>t,getPageLinkConfig:({frontmatter:t})=>t.prev}};function c(t,{$themeConfig:e,$page:a,$route:s,$site:r,sidebarItems:l}){const{resolveLink:u,getThemeLinkConfig:c,getPageLinkConfig:p}=t,d=c(e),h=p(a),f=o()(h)?d:h;return!1===f?void 0:i()(f)?Object(n.k)(r.pages,f,s.path):u(a,l)}function p(t,e,a){const n=[];!function t(e,a){for(let n=0,s=e.length;n({isSidebarOpen:!1}),computed:{shouldShowNavbar(){const{themeConfig:t}=this.$site,{frontmatter:e}=this.$page;return!1!==e.navbar&&!1!==t.navbar&&(this.$title||t.logo||t.repo||t.nav||this.$themeLocaleConfig.nav)},shouldShowSidebar(){const{frontmatter:t}=this.$page;return!t.home&&!1!==t.sidebar&&this.sidebarItems.length},sidebarItems(){return Object(o.l)(this.$page,this.$page.regularPath,this.$site,this.$localePath)},pageClasses(){const t=this.$page.frontmatter.pageClass;return[{"no-navbar":!this.shouldShowNavbar,"sidebar-open":this.isSidebarOpen,"no-sidebar":!this.shouldShowSidebar},t]}},mounted(){this.$router.afterEach(()=>{this.isSidebarOpen=!1})},methods:{toggleSidebar(t){this.isSidebarOpen="boolean"==typeof t?t:!this.isSidebarOpen,this.$emit("toggle-sidebar",this.isSidebarOpen)},onTouchStart(t){this.touchStart={x:t.changedTouches[0].clientX,y:t.changedTouches[0].clientY}},onTouchEnd(t){const e=t.changedTouches[0].clientX-this.touchStart.x,a=t.changedTouches[0].clientY-this.touchStart.y;Math.abs(e)>Math.abs(a)&&Math.abs(e)>40&&(e>0&&this.touchStart.x<=80?this.toggleSidebar(!0):this.toggleSidebar(!1))}}},u=a(14),c=Object(u.a)(l,(function(){var t=this,e=t._self._c;return e("div",{staticClass:"theme-container",class:t.pageClasses,on:{touchstart:t.onTouchStart,touchend:t.onTouchEnd}},[t.shouldShowNavbar?e("Navbar",{on:{"toggle-sidebar":t.toggleSidebar}}):t._e(),t._v(" "),e("div",{staticClass:"sidebar-mask",on:{click:function(e){return t.toggleSidebar(!1)}}}),t._v(" "),e("Sidebar",{attrs:{items:t.sidebarItems},on:{"toggle-sidebar":t.toggleSidebar},scopedSlots:t._u([{key:"top",fn:function(){return[t._t("sidebar-top")]},proxy:!0},{key:"bottom",fn:function(){return[t._t("sidebar-bottom")]},proxy:!0}],null,!0)}),t._v(" "),t.$page.frontmatter.home?e("Home"):e("Page",{attrs:{"sidebar-items":t.sidebarItems},scopedSlots:t._u([{key:"top",fn:function(){return[t._t("page-top")]},proxy:!0},{key:"bottom",fn:function(){return[t._t("page-bottom")]},proxy:!0}],null,!0)})],1)}),[],!1,null,null,null);e.default=c.exports}}]); \ No newline at end of file diff --git a/assets/js/20.0d880388.js b/assets/js/20.0d880388.js new file mode 100644 index 0000000..6a6eeea --- /dev/null +++ b/assets/js/20.0d880388.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[20],{258:function(t,c,n){},270:function(t,c,n){"use strict";n(258)},283:function(t,c,n){"use strict";n.r(c);n(270);var i=n(14),s=Object(i.a)({},(function(){var t=this,c=t._self._c;return c("div",{staticClass:"sidebar-button",on:{click:function(c){return t.$emit("toggle-sidebar")}}},[c("svg",{staticClass:"icon",attrs:{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",role:"img",viewBox:"0 0 448 512"}},[c("path",{attrs:{fill:"currentColor",d:"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"}})])])}),[],!1,null,null,null);c.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/21.0ad1d6a3.js b/assets/js/21.0ad1d6a3.js new file mode 100644 index 0000000..3e9fcf5 --- /dev/null +++ b/assets/js/21.0ad1d6a3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[21],{239:function(t,n,e){"use strict";e.d(n,"d",(function(){return r})),e.d(n,"a",(function(){return s})),e.d(n,"i",(function(){return u})),e.d(n,"f",(function(){return a})),e.d(n,"g",(function(){return l})),e.d(n,"h",(function(){return c})),e.d(n,"b",(function(){return f})),e.d(n,"e",(function(){return h})),e.d(n,"k",(function(){return p})),e.d(n,"l",(function(){return d})),e.d(n,"c",(function(){return b})),e.d(n,"j",(function(){return m}));e(90);const r=/#.*$/,i=/\.(md|html)$/,s=/\/$/,u=/^[a-z]+:/i;function o(t){return decodeURI(t).replace(r,"").replace(i,"")}function a(t){return u.test(t)}function l(t){return/^mailto:/.test(t)}function c(t){return/^tel:/.test(t)}function f(t){if(a(t))return t;const n=t.match(r),e=n?n[0]:"",i=o(t);return s.test(i)?t:i+".html"+e}function h(t,n){const e=decodeURIComponent(t.hash),i=function(t){const n=t.match(r);if(n)return n[0]}(n);if(i&&e!==i)return!1;return o(t.path)===o(n)}function p(t,n,e){if(a(n))return{type:"external",path:n};e&&(n=function(t,n,e){const r=t.charAt(0);if("/"===r)return t;if("?"===r||"#"===r)return n+t;const i=n.split("/");e&&i[i.length-1]||i.pop();const s=t.replace(/^\//,"").split("/");for(let t=0;tfunction t(n,e,r,i=1){if("string"==typeof n)return p(e,n,r);if(Array.isArray(n))return Object.assign(p(e,n[0],r),{title:n[1]});{const s=n.children||[];return 0===s.length&&n.path?Object.assign(p(e,n.path,r),{title:n.title}):{type:"group",path:n.path,title:n.title,sidebarDepth:n.sidebarDepth,initialOpenGroupIndex:n.initialOpenGroupIndex,children:s.map(n=>t(n,e,r,i+1)),collapsable:!1!==n.collapsable}}}(t,i,e)):[]}return[]}function g(t){const n=b(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:n.map(n=>({type:"auto",title:n.title,basePath:t.path,path:t.path+"#"+n.slug,children:n.children||[]}))}]}function b(t){let n;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?n=t:n&&(n.children||(n.children=[])).push(t)}),t.filter(t=>2===t.level)}function m(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},241:function(t,n,e){"use strict";e.r(n);var r=e(239),i={name:"NavLink",props:{item:{required:!0}},computed:{link(){return Object(r.b)(this.item.link)},exact(){return this.$site.locales?Object.keys(this.$site.locales).some(t=>t===this.link):"/"===this.link},isNonHttpURI(){return Object(r.g)(this.link)||Object(r.h)(this.link)},isBlankTarget(){return"_blank"===this.target},isInternal(){return!Object(r.f)(this.link)&&!this.isBlankTarget},target(){return this.isNonHttpURI?null:this.item.target?this.item.target:Object(r.f)(this.link)?"_blank":""},rel(){return this.isNonHttpURI||!1===this.item.rel?null:this.item.rel?this.item.rel:this.isBlankTarget?"noopener noreferrer":null}},methods:{focusoutAction(){this.$emit("focusout")}}},s=e(14),u=Object(s.a)(i,(function(){var t=this,n=t._self._c;return t.isInternal?n("RouterLink",{staticClass:"nav-link",attrs:{to:t.link,exact:t.exact},nativeOn:{focusout:function(n){return t.focusoutAction.apply(null,arguments)}}},[t._v("\n "+t._s(t.item.text)+"\n")]):n("a",{staticClass:"nav-link external",attrs:{href:t.link,target:t.target,rel:t.rel},on:{focusout:t.focusoutAction}},[t._v("\n "+t._s(t.item.text)+"\n "),t.isBlankTarget?n("OutboundLink"):t._e()],1)}),[],!1,null,null,null);n.default=u.exports}}]); \ No newline at end of file diff --git a/assets/js/22.b7c97fbe.js b/assets/js/22.b7c97fbe.js new file mode 100644 index 0000000..e581594 --- /dev/null +++ b/assets/js/22.b7c97fbe.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[22],{303:function(t,n,s){"use strict";s.r(n);var e=s(14),o=Object(e.a)({},(function(){return(0,this._self._c)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);n.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/23.94c9df46.js b/assets/js/23.94c9df46.js new file mode 100644 index 0000000..3b009e4 --- /dev/null +++ b/assets/js/23.94c9df46.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[23],{304:function(e,t,r){"use strict";r.r(t);var o=r(14),n=Object(o.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h1",{attrs:{id:"about"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#about"}},[e._v("#")]),e._v(" About")]),e._v(" "),t("p",[t("strong",[e._v("Shardcake")]),e._v(" is a project developed at "),t("a",{attrs:{href:"https://www.devsisters.com",target:"_blank",rel:"noopener noreferrer"}},[t("strong",[e._v("Devsisters")]),t("OutboundLink")],1),e._v(" by the team behind the popular game "),t("a",{attrs:{href:"https://www.cookierun-kingdom.com",target:"_blank",rel:"noopener noreferrer"}},[t("strong",[e._v("Cookie Run: Kingdom")]),t("OutboundLink")],1),e._v(".\nWe have been successfully using this project in production since March 2022.")]),e._v(" "),t("p",[e._v("If you happen to be in South Korea, we are "),t("a",{attrs:{href:"https://careers.devsisters.com/position/detail/?jobPosition=19",target:"_blank",rel:"noopener noreferrer"}},[e._v("recruiting"),t("OutboundLink")],1),e._v("!")]),e._v(" "),t("p",[t("strong",[e._v("Shardcake")]),e._v(" was developed after years of experience with "),t("a",{attrs:{href:"https://doc.akka.io/docs/akka/current/typed/cluster-sharding.html",target:"_blank",rel:"noopener noreferrer"}},[t("strong",[e._v("Akka Cluster Sharding")]),t("OutboundLink")],1),e._v(".\nEven though code has nothing in common with Akka, credits go to them for some ideas and concepts.")]),e._v(" "),t("p",[t("img",{attrs:{src:"/shardcake/kingdom.png",alt:"kingdom image"}})])])}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/24.e86fe2ac.js b/assets/js/24.e86fe2ac.js new file mode 100644 index 0000000..028ea71 --- /dev/null +++ b/assets/js/24.e86fe2ac.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[24],{305:function(t,s,e){"use strict";e.r(s);var a=e(14),n=Object(a.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"getting-started"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#getting-started"}},[t._v("#")]),t._v(" Getting Started")]),t._v(" "),s("p",[s("strong",[t._v("Shardcake")]),t._v(" is a "),s("strong",[t._v("Scala open source library")]),t._v(" that makes it easy to distribute entities across multiple servers and interact with those entities using their ID without knowing their actual location (this is also known as "),s("em",[t._v("location transparency")]),t._v(").")]),t._v(" "),s("p",[t._v("Shardcake exposes a "),s("strong",[t._v("purely functional API")]),t._v(" and depends heavily on "),s("a",{attrs:{href:"https://zio.dev",target:"_blank",rel:"noopener noreferrer"}},[t._v("ZIO"),s("OutboundLink")],1),t._v(". It is recommended to be familiar with ZIO to read this documentation.")]),t._v(" "),s("h2",{attrs:{id:"a-simple-use-case"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#a-simple-use-case"}},[t._v("#")]),t._v(" A simple use case")]),t._v(" "),s("p",[t._v("We are building a multiplayer game where "),s("strong",[t._v("users")]),t._v(" can join "),s("strong",[t._v("guilds")]),t._v(".\nWe expect our game to be successful, so we need to be able to scale out (deploy it on multiple servers to handle the load).")]),t._v(" "),s("p",[t._v("A guild is limited to 30 members.\nLet's consider the case where 2 users try to join a guild at the exact same time, but our guild already had 29 members.")]),t._v(" "),s("p",[s("img",{attrs:{src:"/shardcake/usecase1.png",alt:"naive diagram"}})]),t._v(" "),s("p",[t._v("If we implement this naively, 2 different servers might receive our 2 requests to join the guild.\nThey will both check the current size of the guild at the same time, which will be 29 and they will both accept the new guild member. Now our guild has 31 members 😱.")]),t._v(" "),s("p",[t._v("There are 2 common approaches to deal with this issue:")]),t._v(" "),s("ul",[s("li",[s("strong",[t._v("Global lock")]),t._v(" approach: when checking the members of the guild, acquire a lock that is shared between the different game servers, and release it after saving the new member.\nThat way, if 2 different game servers try to do it at the same time, the 2nd one will wait for the first one to finish.")]),t._v(" "),s("li",[s("strong",[t._v("Single writer")]),t._v(" approach: instead of handling the requests on 2 game servers concurrently, redirect the 2 requests to a single entity that will handle them sequentially.")])]),t._v(" "),s("p",[s("strong",[t._v("Entity Sharding")]),t._v(" is a way to implement the second approach. In this case our entities (here, our guilds) will be spread across our game servers so that\neach entity exists in only one place at the time.")]),t._v(" "),s("p",[s("img",{attrs:{src:"/shardcake/usecase2.png",alt:"single writer diagram"}})]),t._v(" "),s("p",[t._v("If our entity might be on any game server, how do we know where it is? This is the second characteristic of entity sharding, known as location transparency: we only need to know the entity ID.\nUsing the entity ID, the sharding system will be able to find on which server the entity is located.")]),t._v(" "),s("p",[s("strong",[t._v("Shardcake")]),t._v(" provides components to:")]),t._v(" "),s("ul",[s("li",[t._v("automatically manage the assignments of entities to game servers")]),t._v(" "),s("li",[t._v("send messages to your entities using their IDs")])]),t._v(" "),s("p",[t._v("Once sharding is setup, it will let you write code like the following (sending a message to guild regardless of its location):")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" joinGuild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("guildId"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" GuildId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Throwable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" GuildState"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n userId "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" getCurrentUserFromContext\n guildState "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" guild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("guildId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("JoinGuild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("userId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" guildState\n")])])]),s("h2",{attrs:{id:"terminology"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#terminology"}},[t._v("#")]),t._v(" Terminology")]),t._v(" "),s("p",[t._v("Before we go further, let's define a few terms:")]),t._v(" "),s("ul",[s("li",[t._v("An "),s("strong",[t._v("entity")]),t._v(" is a small message handler that can be addressed by ID.\nFor example, "),s("code",[t._v("User A")]),t._v(" or "),s("code",[t._v("Guild B")]),t._v(" are entities. In this case we say that "),s("code",[t._v("User")]),t._v(" and "),s("code",[t._v("Guild")]),t._v(" are "),s("strong",[t._v("entity types")]),t._v(".")]),t._v(" "),s("li",[t._v("A "),s("strong",[t._v("pod")]),t._v(" is an application server that can host entities.\nEntities usually run on multiple pods, but a single entity will only run on a single pod at the time. You will never have the same entity running on 2 different pods.")]),t._v(" "),s("li",[t._v("A "),s("strong",[t._v("shard")]),t._v(" is a logical group of entities that will always be located on the same pod.\nThere might be millions of entities, so instead of keeping millions of entities-to-pods mappings, we group entities into shards and maintain reasonably-sized shards-to-pods mappings.")])]),t._v(" "),s("h2",{attrs:{id:"key-components"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#key-components"}},[t._v("#")]),t._v(" Key components")]),t._v(" "),s("p",[t._v("Shardcake is composed of 2 main components:")]),t._v(" "),s("ul",[s("li",[t._v("The "),s("strong",[t._v("Shard Manager")]),t._v(" is an independent component that needs a single instance running. It is in charge of assigning shards to pods.")]),t._v(" "),s("li",[s("strong",[t._v("Entities")]),t._v(" will run on your application servers and process messages that are sent to them. Note that the entity behavior (including entity persistence) is entirely up to you.\nShardcake only takes care of starting entities on the right pods as well as the communication between them.")])]),t._v(" "),s("p",[t._v("There are 4 pluggable parts that can be implemented with the technology of your choice.")]),t._v(" "),s("ul",[s("li",[t._v("The "),s("code",[t._v("Storage")]),t._v(" trait defines where shard assignments will be stored. Shardcake provides an implementation using "),s("strong",[t._v("Redis")]),t._v(".")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("Pods")]),t._v(" trait defines how to communicate with remote pods. Shardcake provides an implementation using "),s("strong",[t._v("gRPC")]),t._v(" as the protocol.")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("Serialization")]),t._v(" defines how to encode and decode messages. Shardcake provides an implementation using "),s("strong",[t._v("Kryo")]),t._v(".")]),t._v(" "),s("li",[t._v("The "),s("code",[t._v("PodsHealth")]),t._v(" trait defines how to check if a pod is healthy or not. Shardcake provides an implementation using the "),s("strong",[t._v("k8s API")]),t._v(".")])]),t._v(" "),s("p",[s("img",{attrs:{src:"/shardcake/arch.png",alt:"architecture diagram"}})]),t._v(" "),s("h2",{attrs:{id:"an-example"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#an-example"}},[t._v("#")]),t._v(" An example")]),t._v(" "),s("h3",{attrs:{id:"shard-manager"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#shard-manager"}},[t._v("#")]),t._v(" Shard Manager")]),t._v(" "),s("p",[t._v("The first thing we need to do is starting the Shard Manager. This component is available by importing the "),s("code",[t._v("shardcake-manager")]),t._v(" dependency, and requires implementations of "),s("code",[t._v("Storage")]),t._v(", "),s("code",[t._v("Pods")]),t._v(" and "),s("code",[t._v("PodsHealth")]),t._v(" to work.")]),t._v(" "),s("p",[t._v("To make it simpler and run our example without 3rd parties, we're going to run a simple "),s("code",[t._v("PodsHealth")]),t._v(" implementation that just pings a pod to see if it's alive, and in-memory "),s("code",[t._v("Storage")]),t._v(".\nWe need a proper messaging protocol to communicate with pods, so we're going to use "),s("code",[t._v("shardcake-protocol-grpc")]),t._v(".")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('libraryDependencies += "com.devsisters" %% "shardcake-manager" % "2.1.0"\nlibraryDependencies += "com.devsisters" %% "shardcake-protocol-grpc" % "2.1.0"\n')])])]),s("p",[t._v("The Shard Manager exposes a small GraphQL API, which means we need to start a small webserver. This can be done by calling "),s("code",[t._v("Server.run")]),t._v(" and providing all the required dependencies.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("com"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("devsisters"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("shardcake"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("_\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("com"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("devsisters"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("shardcake"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("interfaces"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("_\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("zio"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("_\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" ShardManagerApp "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" ZIOAppDefault "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" run"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n Server"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("run"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("provide"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n ZLayer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("succeed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ManagerConfig"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("default"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n ZLayer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("succeed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GrpcConfig"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("default"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n PodsHealth"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("local"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// just ping a pod to see if it's alive")]),t._v("\n GrpcPods"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("live"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// use gRPC protocol")]),t._v("\n Storage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("memory"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// store data in memory")]),t._v("\n ShardManager"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("live "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// shard manager logic")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("That's it! Running this small app will start the Shard Manager and its API. It is now ready to receive registration from pods.")]),t._v(" "),s("h3",{attrs:{id:"entity-behavior"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#entity-behavior"}},[t._v("#")]),t._v(" Entity Behavior")]),t._v(" "),s("p",[t._v("We now need to define our "),s("strong",[t._v("entity behavior")]),t._v(": what kind of messages can our entity receive, and what to do when it receives those messages. We will model our "),s("strong",[t._v("Guild")]),t._v(" example.")]),t._v(" "),s("p",[t._v("First, we need the following dependencies:")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('libraryDependencies += "com.devsisters" %% "shardcake-entities" % "2.1.0"\nlibraryDependencies += "com.devsisters" %% "shardcake-protocol-grpc" % "2.1.0"\n')])])]),s("p",[t._v("Let's start with defining the messages our entities can receive. We will have 2: one for joining a guild and one for leaving.\nWe create a "),s("code",[t._v("sealed trait")]),t._v(" that will contain all the possible message types.\nAny message that can be replied needs to contain a "),s("code",[t._v("Replier[A]")]),t._v(" where "),s("code",[t._v("A")]),t._v(" is the response type.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("sealed")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" GuildMessage\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" GuildMessage "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Join"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("userId"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" replier"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Replier"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Try"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Set"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" GuildMessage\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" Leave"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("userId"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" GuildMessage\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("We also need to define an "),s("strong",[t._v("Entity Type")]),t._v(". This is done by extending "),s("code",[t._v("EntityType")]),t._v(" with the message type as well as a unique "),s("code",[t._v("String")]),t._v(" identifier for this type.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" Guild "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("extends")]),t._v(" EntityType"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("GuildMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"guild"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("The behavior itself is a function with the following signature:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" behavior"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("entityId"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" messages"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Queue"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("GuildMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" RIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Sharding"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n")])])]),s("p",[t._v("It takes an "),s("code",[t._v("entityId")]),t._v(" and a "),s("code",[t._v("Queue[GuildMessage]")]),t._v(" and returns a "),s("code",[t._v("ZIO")]),t._v(" that never ends (hence the return type "),s("code",[t._v("Nothing")]),t._v(").\nThat function is just supposed to consume "),s("code",[t._v("messages")]),t._v(" forever.")]),t._v(" "),s("p",[t._v("Let's first define how to handle a single "),s("code",[t._v("GuildMessage")]),t._v(".")]),t._v(" "),s("p",[t._v("We will use a "),s("code",[t._v("Ref[Set[String]]")]),t._v(" to hold our state (the list of users in the guild).\nWhen we receive a "),s("code",[t._v("Join")]),t._v(" request, we check if the guild is already full (here, we hardcode the max to 5) and respond with a failure or modify the state and respond with a success.\nWe use "),s("code",[t._v("replier.reply")]),t._v(" to send a response to the sender (here, sending the full list of guild members wrapped in a "),s("code",[t._v("Try")]),t._v(" to express errors).")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" handleMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("state"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Ref"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Set"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" message"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" GuildMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" RIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Sharding"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n message "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("match")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" GuildMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Join"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("userId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" replier"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v("\n state"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("get"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("flatMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("members "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("members"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("size "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n replier"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("reply"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Failure"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" Exception"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Guild is already full!"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v("\n state"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("updateAndGet"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_ "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" userId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("flatMap "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" newMembers "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v("\n replier"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("reply"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Success"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("newMembers"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" GuildMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Leave"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("userId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v("\n state"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("update"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("_ "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" userId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("We are now ready to create our behavior, starting from an empty state when the entity is created:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" behavior"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("entityId"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" messages"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Queue"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("GuildMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" RIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Sharding"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Nothing")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n Ref\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("make"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Set"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("empty"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("flatMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("state "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" messages"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("take"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("flatMap"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("handleMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("state"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("forever"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("h3",{attrs:{id:"run-the-application"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#run-the-application"}},[t._v("#")]),t._v(" Run the application")]),t._v(" "),s("p",[t._v("To run our entities, we need to register their behavior to the Sharding system, which is done by calling "),s("code",[t._v("Sharding.registerEntity")]),t._v(", passing the entity type and the behavior.")]),t._v(" "),s("p",[t._v("We then need to notify the Shard Manager that a new pod is now ready to run entities, and shards may be assigned to it.\nThis is done by calling "),s("code",[t._v("Sharding.registerScoped")]),t._v(" (which is the equivalent of calling "),s("code",[t._v("Sharding.register")]),t._v(" when the program starts and "),s("code",[t._v("Sharding.unregister")]),t._v(" when the program ends).")]),t._v(" "),s("p",[t._v("To communicate with our entities, we need a "),s("code",[t._v("Messenger[GuildMessage]")]),t._v(", which we get by calling "),s("code",[t._v("Sharding.messenger")]),t._v(". You can even use "),s("code",[t._v("messenger")]),t._v(" on a pod that is not hosting any entities.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" program "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" Sharding"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("registerEntity"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Guild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" behavior"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" Sharding"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("registerScoped\n guild "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" Sharding"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("messenger"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Guild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" guild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"guild1"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Join"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"user1"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debug\n _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" guild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"guild1"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Join"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"user2"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debug\n _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" guild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"guild1"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Join"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"user3"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debug\n _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" guild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"guild1"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Join"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"user4"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debug\n _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" guild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"guild1"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Join"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"user5"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debug\n _ "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" guild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("send"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"guild1"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Join"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"user6"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" _"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("debug\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("Finally, we provide all required dependencies as we did for the Shard Manager.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" run"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n ZIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("scoped"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("program"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("provide"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n ZLayer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("succeed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Config"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("default"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n ZLayer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("succeed"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GrpcConfig"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("default"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Serialization"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("javaSerialization"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// use java serialization for messages")]),t._v("\n Storage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("memory"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// store data in memory")]),t._v("\n ShardManagerClient"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("liveWithSttp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// client to communicate with the Shard Manager")]),t._v("\n GrpcPods"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("live"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// use gRPC protocol")]),t._v("\n GrpcShardingService"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("live"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// expose gRPC service")]),t._v("\n Sharding"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("live "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// sharding logic")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("We can now run our program. It will successively send 6 "),s("code",[t._v("Join")]),t._v(" messages to our guild and print the result:")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("Success(Set(user1))\nSuccess(Set(user1, user2))\nSuccess(Set(user1, user2, user3))\nSuccess(Set(user1, user2, user3, user4))\nSuccess(HashSet(user1, user5, user4, user2, user3))\nFailure(java.lang.Exception: Guild is already full!)\n")])])]),s("h4",{attrs:{id:"one-more-thing"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#one-more-thing"}},[t._v("#")]),t._v(" One more thing...")]),t._v(" "),s("p",[s("code",[t._v("Sharding")]),t._v(" also exposes 2 methods that can be interesting for some use cases:")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("registerSingleton")]),t._v(" lets you register some background processes that will run in only one pod at any given time. Singletons can't receive messages and they are not stopped in case of inactivity.")]),t._v(" "),s("li",[s("code",[t._v("registerTopic")]),t._v(" lets you broadcast messages to all registered pods (use "),s("code",[t._v("Sharding.broadcaster")]),t._v(" instead of "),s("code",[t._v("Sharding.messenger")]),t._v(").")])]),t._v(" "),s("h3",{attrs:{id:"where-to-go-from-there"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#where-to-go-from-there"}},[t._v("#")]),t._v(" Where to go from there?")]),t._v(" "),s("p",[t._v("In this simple example, we only had a single pod so there was no real benefit from using sharding.\nBut we could run the exact same code on multiple pods, and the sharding system would ensure that each guild is only running on a single pod.\nIn other words, all messages sent to "),s("code",[t._v("guild1")]),t._v(" would be handled by the same pod.")]),t._v(" "),s("p",[t._v("The only changes we would need to make would be:")]),t._v(" "),s("ul",[s("li",[t._v("to use an actual "),s("code",[t._v("Storage")]),t._v(" implementation for sharding (e.g. Redis)")]),t._v(" "),s("li",[t._v("to save our guild state somewhere instead of in memory, so that we don't lose this state if the guild entity is moved from one pod to another")])]),t._v(" "),s("p",[t._v("The running code for this example "),s("a",{attrs:{href:"https://github.com/devsisters/shardcake/tree/series/2.x/examples/src/main/scala/example/simple",target:"_blank",rel:"noopener noreferrer"}},[t._v("can be found here"),s("OutboundLink")],1),t._v(",\nas well as "),s("a",{attrs:{href:"https://github.com/devsisters/shardcake/tree/series/2.x/examples/src/main/scala/example/complex",target:"_blank",rel:"noopener noreferrer"}},[t._v("a more complex example"),s("OutboundLink")],1),t._v(" using Redis to persist data and where you can run multiple pods.")]),t._v(" "),s("p",[t._v("To understand how Sharding works under the hood, have a look at the "),s("RouterLink",{attrs:{to:"/docs/architecture.html"}},[t._v("Architecture")]),t._v(" section.\nThe "),s("RouterLink",{attrs:{to:"/docs/config.html"}},[t._v("Configuration")]),t._v(" section explains how to configure the sharding system.\nFinally, the "),s("RouterLink",{attrs:{to:"/docs/customization.html"}},[t._v("Customization")]),t._v(" section describes how you can use your own storage, serialization or messaging protocol, as well as the options provided by Shardcake.")],1),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("Differences with Akka Cluster Sharding ?")]),t._v(" "),s("p",[s("a",{attrs:{href:"https://doc.akka.io/docs/akka/current/typed/cluster-sharding.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("Akka Cluster Sharding"),s("OutboundLink")],1),t._v(" is the main alternative for sharding in Scala.\nHere is how Shardcake differs from Akka:")]),t._v(" "),s("ul",[s("li",[s("p",[t._v("Shardcake is a "),s("strong",[t._v("purely functional library")]),t._v(" based on "),s("code",[t._v("ZIO")]),t._v(', while Akka is a library leaning towards the "better Java" style of Scala and is based on '),s("code",[t._v("Future")]),t._v(".")])]),t._v(" "),s("li",[s("p",[t._v("With Akka, all application servers "),s("strong",[t._v("must be part of a cluster")]),t._v(". This is particularly tricky to configure and sensitive to all kinds of failures.\nThis is not the case with Shardcake since the Shard Manager is an external component that relies on systems like Kubernetes to monitor pod health.")])]),t._v(" "),s("li",[s("p",[t._v("Akka is way more than a sharding library, this is basically an entire framework that has a lot of features.\nShardcake is much "),s("strong",[t._v("simpler and modular")]),t._v(". It lets you customize multiple things, including the messaging protocol between pods.")])]),t._v(" "),s("li",[s("p",[t._v("Akka is backed by "),s("a",{attrs:{href:"https://www.lightbend.com",target:"_blank",rel:"noopener noreferrer"}},[t._v("Lightbend"),s("OutboundLink")],1),t._v(", who provides "),s("strong",[t._v("commercial support")]),t._v(",\nwhile Shardcake is backed by "),s("a",{attrs:{href:"https://www.devsisters.com",target:"_blank",rel:"noopener noreferrer"}},[t._v("Devsisters"),s("OutboundLink")],1),t._v(" and the "),s("strong",[t._v("ZIO community")]),t._v(".")])])])])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/25.d539dff2.js b/assets/js/25.d539dff2.js new file mode 100644 index 0000000..955b879 --- /dev/null +++ b/assets/js/25.d539dff2.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{306:function(e,t,a){"use strict";a.r(t);var s=a(14),n=Object(s.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h1",{attrs:{id:"architecture"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#architecture"}},[e._v("#")]),e._v(" Architecture")]),e._v(" "),t("p",[e._v("Make sure to read the "),t("RouterLink",{attrs:{to:"/docs/#getting-started"}},[e._v("Getting Started")]),e._v(" and "),t("RouterLink",{attrs:{to:"/docs/#terminology"}},[e._v("Terminology")]),e._v(" first.")],1),e._v(" "),t("h2",{attrs:{id:"main-concepts"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#main-concepts"}},[e._v("#")]),e._v(" Main Concepts")]),e._v(" "),t("p",[t("img",{attrs:{src:"/shardcake/arch2.png",alt:"architecture diagram"}})]),e._v(" "),t("ul",[t("li",[e._v("The "),t("strong",[e._v("Shard Manager")]),e._v(" is a single node in charge of maintaining the pods <-> shards assignments.\nOnly one Shard Manager should be alive at any given time.")]),e._v(" "),t("li",[e._v("Pods "),t("strong",[e._v("register")]),e._v(" to the Shard Manager when they start and "),t("strong",[e._v("unregister")]),e._v(" when they stop.")]),e._v(" "),t("li",[e._v("Pods "),t("strong",[e._v("read the shard assignments")]),e._v(" from the Storage layer (including updates) and cache them locally.\nWhen the Shard Manager "),t("strong",[e._v("assigns")]),e._v(" or "),t("strong",[e._v("unassigns")]),e._v(" shards to/from pods, it notifies them directly.")]),e._v(" "),t("li",[e._v("When a pod "),t("strong",[e._v("sends a message")]),e._v(" to a given entity,\nit checks which shard this entity belongs to and redirects the message to the pod in charge of that shard.\nThat pod will then start the entity behavior locally if it was not already started.")]),e._v(" "),t("li",[e._v("The "),t("strong",[e._v("Shard ID")]),e._v(" assigned to an entity is calculated as follows:\n"),t("code",[e._v("shardId = abs(entityId.hashCode % numberOfShards) + 1")]),e._v(". In other words, it is a stable number between 1 and the number of shards.")]),e._v(" "),t("li",[e._v("When a pod is "),t("strong",[e._v("unresponsive")]),e._v(", other pods will notify the Shard Manager, who will check with the Health API if the pod is still alive.\nIt will then unassign shards from this pod only if the pod is not alive\n(as long as it’s alive, we can’t reassign its shards because it might cause an entity to be alive in 2 different pods).")])]),e._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[e._v("No single point of failure")]),e._v(" "),t("p",[e._v("The Shard Manager is actually involved only when pods are added or removed.")]),e._v(" "),t("p",[e._v("The rest of the time, it does nothing since pods have a cached version of shard assignments and communicate between themselves directly.\nThat means they are able to work even if the Shard Manager is down.")])]),e._v(" "),t("h2",{attrs:{id:"detailed-flows"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#detailed-flows"}},[e._v("#")]),e._v(" Detailed flows")]),e._v(" "),t("h3",{attrs:{id:"pod-start-sharding-register"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#pod-start-sharding-register"}},[e._v("#")]),e._v(" Pod Start ("),t("code",[e._v("Sharding#register")]),e._v(")")]),e._v(" "),t("ol",[t("li",[e._v("Pod gets shard assignments from the Storage layer.")]),e._v(" "),t("li",[e._v("Pod exposes the sharding API (e.g. starts a gRPC server) so that it can be contacted by the Shard Manager and other pods.")]),e._v(" "),t("li",[e._v("Pod calls the "),t("code",[e._v("register")]),e._v(" endpoint of the Shard Manager. This will eventually trigger a rebalance, and some shards may be assigned to this pod.\nWhen that happens, the Shard Manager will call the "),t("code",[e._v("assign")]),e._v(" API of this pod.")])]),e._v(" "),t("h3",{attrs:{id:"pod-stop-sharding-unregister"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#pod-stop-sharding-unregister"}},[e._v("#")]),e._v(" Pod Stop ("),t("code",[e._v("Sharding#unregister")]),e._v(")")]),e._v(" "),t("ol",[t("li",[e._v("Pod prevents any new entity to be started locally. Any message received from other pods will return the "),t("code",[e._v("EntityNotManagedByThisPod")]),e._v(" error and be retried.")]),e._v(" "),t("li",[e._v("Pod stops all local entities by sending them the termination message. Messages currently in progress are processed.")]),e._v(" "),t("li",[e._v("Once all entities are stopped, pod calls the "),t("code",[e._v("unregister")]),e._v(" endpoint of the Shard Manager. This will trigger an instant rebalance, so that shards handled by this pod will be assigned to other pods.")]),e._v(" "),t("li",[e._v("Pod stops the sharding API and is ready to stop.")])]),e._v(" "),t("h3",{attrs:{id:"sending-a-message-to-an-entity-messenger-send"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#sending-a-message-to-an-entity-messenger-send"}},[e._v("#")]),e._v(" Sending a message to an entity ("),t("code",[e._v("Messenger#send")]),e._v(")")]),e._v(" "),t("ol",[t("li",[e._v("Pod1 needs to send a message to a given entity.")]),e._v(" "),t("li",[e._v("Pod1 calculates the shard ID for that entity.")]),e._v(" "),t("li",[e._v("Pod1 checks in local shard assignments cache which pod is responsible for this entity. It is Pod2. If Pod1 == Pod2, the next step is skipped.")]),e._v(" "),t("li",[e._v("Pod1 calls the "),t("code",[e._v("sendMessage")]),e._v(" endpoint of Pod2's sharding API.")]),e._v(" "),t("li",[e._v("Pod2 redirects the message to its "),t("code",[e._v("EntityManager")])]),e._v(" "),t("li",[e._v("Pod2 "),t("code",[e._v("EntityManager")]),e._v(" calculates the shard ID for that entity and checks that this shards ID should be handled locally. If not, it returns the "),t("code",[e._v("EntityNotManagedByThisPod")]),e._v(" error.")]),e._v(" "),t("li",[e._v("Pod2 starts the entity if it was not already started and forward the message to it.")]),e._v(" "),t("li",[e._v("When a response is produced, the response is sent back to Pod1.")])]),e._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[e._v("Unresponsive Pod")]),e._v(" "),t("p",[e._v("If Pod2 is not responsive, Pod1 will call the "),t("code",[e._v("notifyUnhealthyPod")]),e._v(" endpoint of the Shard Manager.\nThe Shard Manager will check with the Health API (e.g. Kubernetes) if the pod is still alive and unregister it if not.\nPod1 will keep retrying and hopefully a new pod will be assigned very soon.")])]),e._v(" "),t("h3",{attrs:{id:"rebalance-algorithm"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#rebalance-algorithm"}},[e._v("#")]),e._v(" Rebalance Algorithm")]),e._v(" "),t("p",[e._v("The rebalance process is the action of assigning or unassigning shards from/to pods.\nIt is triggered:")]),e._v(" "),t("ul",[t("li",[e._v("when the first pod registers (it will be assigned all shards)")]),e._v(" "),t("li",[e._v("when a pod unregisters")]),e._v(" "),t("li",[e._v("on a regular interval defined by "),t("code",[e._v("rebalanceInterval")]),e._v(" (see "),t("RouterLink",{attrs:{to:"/docs/config.html#shard-manager-configuration"}},[e._v("Config")]),e._v(")")],1)]),e._v(" "),t("p",[e._v("The workflow is designed so that a shard can never be assigned to 2 different pods at the same time.")]),e._v(" "),t("ol",[t("li",[e._v("Shard Manager decides which shards should be assigned and unassigned (see "),t("a",{attrs:{href:"#assignment-algorithm"}},[e._v("Assignment Algorithm")]),e._v(").")]),e._v(" "),t("li",[e._v("Shard Manager pings all pods involved in assignments and unassignments to check that they are responsive (default timeout = 3s, defined by "),t("code",[e._v("pingTimeout")]),e._v(" (see "),t("RouterLink",{attrs:{to:"/docs/config.html#shard-manager-configuration"}},[e._v("Config")]),e._v(")).\nThis step is to avoid dealing with dead nodes, which would slow down the rebalance process.\nUnresponsive pods are removed from assignments and unassignments.\nPings are done in parallel.")],1),e._v(" "),t("li",[e._v("Shard Manager calls the "),t("code",[e._v("unassign")]),e._v(" endpoint of all pods that have shards to unassign.\nThis is done in parallel.\nWhen "),t("code",[e._v("unassign")]),e._v(" succeeds, it means all local entities on those shards have been stopped and are ready to be reassigned.")]),e._v(" "),t("li",[e._v("Any shard that failed to be unassigned is removed from the assignment list.")]),e._v(" "),t("li",[e._v("Shard Manager calls the "),t("code",[e._v("assign")]),e._v(" endpoint of all pods that have shards to assign.\nThis is done in parallel.")]),e._v(" "),t("li",[e._v("If there were any pods that failed to be pinged, unassigned or assigned, we check with the Health API if those pods are still alive.\nIf they are not, we unregister them and trigger another rebalance immediately after.")]),e._v(" "),t("li",[e._v("Shard Manager persists the new assignments to the chosen Storage and pods get notified of the changes.")]),e._v(" "),t("li",[e._v("If anything failed, another rebalance will be triggered after a retry interval defined by "),t("code",[e._v("rebalanceRetryInterval")]),e._v(" (see "),t("RouterLink",{attrs:{to:"/docs/config.html#shard-manager-configuration"}},[e._v("Config")]),e._v(").")],1)]),e._v(" "),t("h3",{attrs:{id:"assignment-algorithm"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#assignment-algorithm"}},[e._v("#")]),e._v(" Assignment Algorithm")]),e._v(" "),t("p",[e._v('The assignment algorithm aims at giving the same amount of shards to each pod.\nIt also cares about the pod version, so that in case of a rolling update, shards are assigned in priority to "new" pods in order to limit the number of assignments.')]),e._v(" "),t("ol",[t("li",[e._v("Shard Manager calculates the average number of shards per pod ("),t("code",[e._v("number of shards / number of pods")]),e._v(").")]),e._v(" "),t("li",[e._v("For each pod: if the number of shards assigned to this pod is higher than the average, randomly pick a number of these shards equal to the difference.\nThose are called "),t("code",[e._v("extraShardsToAllocate")]),e._v(".\nIf some pods have different versions, "),t("code",[e._v("extraShardsToAllocate")]),e._v(" is set to empty (it means we’re in the middle of a rolling update, we don’t want to rebalance).")]),e._v(" "),t("li",[e._v("Shards to rebalance = unassigned shards + "),t("code",[e._v("extraShardsToAllocate")]),e._v(".\nWe sort them to handle unassigned shards first, then shards on the pods with most shards, then shards on old pods.")]),e._v(" "),t("li",[e._v("For each shard to rebalance:")]),e._v(" "),t("li",[e._v("Find the pod that has the least amount of shards. Pods that don’t have the latest version are excluded from this search (we don’t want to assign shards to old pods since they will be stopped soon).")]),e._v(" "),t("li",[e._v("If that pod is the pod the shard is currently assigned to, don’t create any assignments.")]),e._v(" "),t("li",[e._v("If that pod has only 1 less shard than the currently assigned pod, don’t create any assignments (not worth rebalancing if they have only 1 difference).")]),e._v(" "),t("li",[e._v("Otherwise, create a new assignment for that shard to that pod, and an unassignment for that shard from its previous pod (if any).")])])])}),[],!1,null,null,null);t.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/26.e0fa533b.js b/assets/js/26.e0fa533b.js new file mode 100644 index 0000000..b8e7c93 --- /dev/null +++ b/assets/js/26.e0fa533b.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{308:function(e,t,a){"use strict";a.r(t);var s=a(14),o=Object(s.a)({},(function(){var e=this,t=e._self._c;return t("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[t("h1",{attrs:{id:"configuration"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#configuration"}},[e._v("#")]),e._v(" Configuration")]),e._v(" "),t("h2",{attrs:{id:"sharding-configuration"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#sharding-configuration"}},[e._v("#")]),e._v(" Sharding Configuration")]),e._v(" "),t("p",[e._v("Here's the list of thing you can configure on the pod side:")]),e._v(" "),t("ul",[t("li",[t("code",[e._v("numberOfShards")]),e._v(": number of shards")])]),e._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[e._v("How to choose the number of shards?")]),e._v(" "),t("p",[t("code",[e._v("numberOfShards")]),e._v(" is used to calculate the shard ID from the entity ID. For that reason, it should be the same on all pods, and can not be changed while the app is running.")]),e._v(" "),t("p",[e._v("If this value is lower than the number of pods, some pods will have no shards (and host no entities), which is inefficient.\nIf this value is too low, a difference of 1 shard between 2 pods will be quite significant and introduce some unbalance between the number of entities hosted on each pod.\nOn the other hand, if there are too many shards, each pod will have a lot of shards, which will produce an unnecessary overhead.")]),e._v(" "),t("p",[e._v("A good rule of thumb is to set the number of shards to 10x the maximum number of pods that you expect to have.")])]),e._v(" "),t("ul",[t("li",[t("code",[e._v("selfHost")]),e._v(": hostname or IP address of the current pod")]),e._v(" "),t("li",[t("code",[e._v("shardingPort")]),e._v(": port used for pods to communicate together")]),e._v(" "),t("li",[t("code",[e._v("shardManagerUri")]),e._v(": url of the Shard Manager GraphQL API")]),e._v(" "),t("li",[t("code",[e._v("serverVersion")]),e._v(": version of the current pod")])]),e._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[e._v("What's the version?")]),e._v(" "),t("p",[e._v("When performing a rolling update (upgrading all pods one by one without downtime), we want to avoid assigning shards to pods that will be stopped soon.\nIdeally, we want to move each shard only once during the whole process.")]),e._v(" "),t("p",[e._v("This "),t("code",[e._v("serverVersion")]),e._v(" allows the Shard Manager to know which pods are old and which are new. It will then pick the new pods to assign shards.")])]),e._v(" "),t("ul",[t("li",[t("code",[e._v("entityMaxIdleTime")]),e._v(": time of inactivity (without receiving any message) after which an entity will be stopped")])]),e._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[e._v("Termination Message")]),e._v(" "),t("p",[t("code",[e._v("registerEntity")]),e._v(" takes an optional parameter called "),t("code",[e._v("terminationMessage")]),e._v(". This allows defining a message that will be sent to your entities before they are stopped (either by a rebalance or because of inactivity).")]),e._v(" "),t("p",[e._v('If no termination message is provided, the entity queues will simply be shutdown.\nBut if you want to ensure entities are stopped "cleanly" after processing their last message, define a termination message and stop the behavior yourself (by calling '),t("code",[e._v("ZIO.interrupt")]),e._v(") after receiving that message.")]),e._v(" "),t("p",[e._v("Termination messages must contain a promise which you need to complete to indicate that shutdown is complete. See the "),t("a",{attrs:{href:"https://github.com/devsisters/shardcake/tree/series/2.x/examples/src/main/scala/example/complex",target:"_blank",rel:"noopener noreferrer"}},[e._v("example here"),t("OutboundLink")],1),e._v(".")])]),e._v(" "),t("ul",[t("li",[t("code",[e._v("entityTerminationTimeout")]),e._v(": time we give to an entity to handle the termination message before interrupting it")]),e._v(" "),t("li",[t("code",[e._v("sendTimeout")]),e._v(": timeout when calling "),t("code",[e._v("sendMessage")])]),e._v(" "),t("li",[t("code",[e._v("refreshAssignmentsRetryInterval")]),e._v(": retry interval in case of failure getting shard assignments from storage")]),e._v(" "),t("li",[t("code",[e._v("unhealthyPodReportInterval")]),e._v(": interval to report unhealthy pods to the Shard Manager (this exists to prevent calling the Shard Manager for each failed message)")]),e._v(" "),t("li",[t("code",[e._v("simulateRemotePods")]),e._v(": disable optimizations when sending a message to an entity hosted on the local shards (this will force serialization of all messages)")])]),e._v(" "),t("h2",{attrs:{id:"shard-manager-configuration"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#shard-manager-configuration"}},[e._v("#")]),e._v(" Shard Manager Configuration")]),e._v(" "),t("p",[e._v("Here's the list of thing you can configure on the Shard Manager side:")]),e._v(" "),t("ul",[t("li",[t("code",[e._v("numberOfShards")]),e._v(": number of shards (see above)")]),e._v(" "),t("li",[t("code",[e._v("apiPort")]),e._v(": port to expose the GraphQL API")]),e._v(" "),t("li",[t("code",[e._v("rebalanceInterval")]),e._v(": interval for regular rebalancing of shards")]),e._v(" "),t("li",[t("code",[e._v("rebalanceRetryInterval")]),e._v(": retry interval for rebalancing when some shards failed to be rebalanced")]),e._v(" "),t("li",[t("code",[e._v("pingTimeout")]),e._v(": time to wait for a pod to respond to a ping request")]),e._v(" "),t("li",[t("code",[e._v("persistRetryInterval")]),e._v(": retry interval for persistence of pods and shard assignments")]),e._v(" "),t("li",[t("code",[e._v("persistRetryCount")]),e._v(": max retry count for persistence of pods and shard assignments")]),e._v(" "),t("li",[t("code",[e._v("rebalanceRate")]),e._v(": max ratio of shards to rebalance in a single iteration")])]),e._v(" "),t("div",{staticClass:"custom-block tip"},[t("p",{staticClass:"custom-block-title"},[e._v("Rebalance Rate")]),e._v(" "),t("p",[e._v("The rebalance rate is there to prevent too many shards being assigned immediately to new pods.\nInstead of reaching a perfect spread right away, we can use several iterations to make sure new pods are able to handle the new shards.\nThis is particularly useful if starting entities has a performance cost (e.g. loading state) and we don't want to start too many at once.")]),e._v(" "),t("p",[e._v("When a pod is leaving, its shards need to be immediately rebalanced so the ratio is not used in that case.")])])])}),[],!1,null,null,null);t.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/27.3eb49986.js b/assets/js/27.3eb49986.js new file mode 100644 index 0000000..f04bdc2 --- /dev/null +++ b/assets/js/27.3eb49986.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[27],{307:function(t,s,a){"use strict";a.r(s);var n=a(14),e=Object(n.a)({},(function(){var t=this,s=t._self._c;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"customization"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#customization"}},[t._v("#")]),t._v(" Customization")]),t._v(" "),s("p",[t._v("As demonstrated in the "),s("RouterLink",{attrs:{to:"/docs/#key-components"}},[t._v("Getting Started")]),t._v(", there are several parts of the system that are entirely customizable.\nFor each of them, Shardcake provides at least a fake implementation for testing and a prod-ready implementation using common technologies.")],1),t._v(" "),s("p",[t._v("Feel free to implement your own!")]),t._v(" "),s("p",[s("img",{attrs:{src:"/shardcake/arch.png",alt:"architecture diagram"}})]),t._v(" "),s("h2",{attrs:{id:"storage"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#storage"}},[t._v("#")]),t._v(" Storage")]),t._v(" "),s("p",[t._v("The "),s("code",[t._v("Storage")]),t._v(" trait defines how to store and access pods and shards assignments.\nIt contains 5 methods: "),s("code",[t._v("getAssignments")]),t._v("/"),s("code",[t._v("saveAssignments")]),t._v(" to store assignments, "),s("code",[t._v("getPods")]),t._v("/"),s("code",[t._v("savePods")]),t._v(" to store pods, and "),s("code",[t._v("assignmentsStream")]),t._v(" to receive assignment updates.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Storage "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" getAssignments"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("ShardId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Option"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" saveAssignments"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("assignments"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("ShardId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Option"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" assignmentsStream"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZStream"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Throwable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Option"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" getPods"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Pod"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" savePods"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pods"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Map"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Pod"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("For testing, you can use the "),s("code",[t._v("Storage.memory")]),t._v(" layer that keeps data in memory.")]),t._v(" "),s("p",[t._v("Shardcake provides an implementation of "),s("code",[t._v("Storage")]),t._v(" using Redis. To use it, add the following dependency:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[t._v("libraryDependencies "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.devsisters"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shardcake-storage-redis"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2.1.0"')]),t._v("\n")])])]),s("p",[t._v("You can then simply use the "),s("code",[t._v("StorageRedis.live")]),t._v(" layer.")]),t._v(" "),s("p",[t._v("It requires a "),s("code",[t._v("RedisConfig")]),t._v(" with the following options:")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("assignmentsKey")]),t._v(": the key to store shard assignments in Redis")]),t._v(" "),s("li",[s("code",[t._v("podsKey")]),t._v(": the key to store registered pods in Redis")])]),t._v(" "),s("p",[t._v("It also requires a "),s("code",[t._v("Redis")]),t._v(" object, which is an alias to "),s("code",[t._v("RedisCommands[Task, String, String] with PubSubCommands[fs2Stream, String, String]")]),t._v(" from the "),s("a",{attrs:{href:"https://redis4cats.profunktor.dev/",target:"_blank",rel:"noopener noreferrer"}},[t._v("redis4cats"),s("OutboundLink")],1),t._v(" library used under the hood.\nHere's an example how to build it:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("com"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("devsisters"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("shardcake"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("StorageRedis"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Redis\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("dev"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("profunktor"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("redis4cats"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("Redis\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("dev"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("profunktor"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("redis4cats"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("connection"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("RedisClient\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("dev"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("profunktor"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("redis4cats"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("data"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("RedisCodec\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("dev"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("profunktor"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("redis4cats"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("effect"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("Log\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("dev"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("profunktor"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("redis4cats"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("pubsub"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("PubSub\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("zio"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("interop"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("catz"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),t._v("_\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("zio"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ZEnvironment"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ZIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ZLayer "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" redis"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZLayer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Throwable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Redis"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("\n ZLayer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("scopedEnvironment "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" runtime"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" zio"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Runtime"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" zio"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Runtime"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("default\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("implicit")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("val")]),t._v(" logger"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Log"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" Log"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" debug"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("msg"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ZIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("unit\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" error"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("msg"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ZIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("logError"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("msg"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" info"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("msg"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("=>")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ZIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("logDebug"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("msg"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n client "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" RedisClient"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("from"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"redis://foobared@localhost"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n commands "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" Redis"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("fromClient"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("client"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" RedisCodec"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Utf8"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n pubSub "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("<-")]),t._v(" PubSub"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mkPubSubConnection"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("String")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("client"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" RedisCodec"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Utf8"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("yield")]),t._v(" ZEnvironment"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("commands"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" pubSub"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("toScopedZIO\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h2",{attrs:{id:"messaging-protocol"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#messaging-protocol"}},[t._v("#")]),t._v(" Messaging Protocol")]),t._v(" "),s("p",[t._v("The "),s("code",[t._v("Pods")]),t._v(" trait defines how to communicate with remote pods.\nIt is used both by the Shard Manager for assigning and unassigning shards, and by pods for internal communication (forward messages to each other).")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Pods "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" assignShards"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pod"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" shards"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Set"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("ShardId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" unassignShards"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pod"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" shards"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Set"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("ShardId"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" ping"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pod"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Unit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n \n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" sendMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pod"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" message"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" BinaryMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Option"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Array"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Byte")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" sendMessageStreaming"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pod"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" message"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" BinaryMessage"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" ZStream"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Throwable"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Array"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Byte")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("For testing, you can use the "),s("code",[t._v("Pods.noop")]),t._v(" layer that does nothing.")]),t._v(" "),s("p",[t._v("Shardcake provides an implementation of "),s("code",[t._v("Pods")]),t._v(" using the gRPC protocol. To use it, add the following dependency:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[t._v("libraryDependencies "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.devsisters"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shardcake-protocol-grpc"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2.1.0"')]),t._v("\n")])])]),s("p",[t._v("You can then simply use the "),s("code",[t._v("GrpcPods.live")]),t._v(" layer.")]),t._v(" "),s("p",[t._v("On pods, you also need expose the gRPC API. This is done by adding the "),s("code",[t._v("GrpcShardingService.live")]),t._v(" layer to your environment. You don't need this one on the Shard Manager.")]),t._v(" "),s("h2",{attrs:{id:"serialization"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#serialization"}},[t._v("#")]),t._v(" Serialization")]),t._v(" "),s("p",[t._v("The "),s("code",[t._v("Serialization")]),t._v(" trait defines how to serialize user messages that will be sent between pods.\nIt contains 2 methods "),s("code",[t._v("encode")]),t._v(" and "),s("code",[t._v("decode")]),t._v(" that define how to transform a give type from and to bytes.")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" Serialization "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" encode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("message"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Any")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Array"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Byte")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" decode"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("A"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("bytes"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Array"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Byte")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Task"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("A"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("For testing, you can use the "),s("code",[t._v("Serialization.javaSerialization")]),t._v(" layer that uses Java Serialization (not recommended in production).")]),t._v(" "),s("p",[t._v("Shardcake provides an implementation of "),s("code",[t._v("Serialization")]),t._v(" using the "),s("a",{attrs:{href:"https://github.com/EsotericSoftware/kryo",target:"_blank",rel:"noopener noreferrer"}},[t._v("Kryo"),s("OutboundLink")],1),t._v(" binary serialization library. To use it, add the following dependency:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[t._v("libraryDependencies "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.devsisters"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shardcake-serialization-kryo"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2.1.0"')]),t._v("\n")])])]),s("p",[t._v("You can then simply use the "),s("code",[t._v("KryoSerialization.live")]),t._v(" layer.")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("Server updates and message versioning")]),t._v(" "),s("ul",[s("li",[t._v("Messages are not persisted, which means that if you stop and restart the whole system, you can change anything in the messages format.")]),t._v(" "),s("li",[t._v("On the other hand, if you wish to do rolling updates (update servers progressively without downtime), you need to be careful with changes in the messages format.")]),t._v(" "),s("li",[t._v("What you can do largely depends on your serialization mechanism, some solutions allow changes while some others are very restrictive.\n"),s("a",{attrs:{href:"https://github.com/EsotericSoftware/kryo",target:"_blank",rel:"noopener noreferrer"}},[t._v("Kryo"),s("OutboundLink")],1),t._v(" by default is pretty strict and won't support most changes, but there are settings to support more (at the cost of some performance or message size).")]),t._v(" "),s("li",[t._v("When you can't modify existing messages, an option is to create new messages that won't be used until the rolling update is finished (so you won't have cases where old nodes receive new messages).")])])]),t._v(" "),s("h2",{attrs:{id:"health"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#health"}},[t._v("#")]),t._v(" Health")]),t._v(" "),s("p",[t._v("The "),s("code",[t._v("PodsHealth")]),t._v(" trait defines how to know if a pod is still alive or is dead (in which case, we should reassign all its shards).")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("trait")]),t._v(" PodsHealth "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("def")]),t._v(" isAlive"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("podAddress"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" PodAddress"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" UIO"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("Boolean")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("For testing, you can use the "),s("code",[t._v("PodsHealth.noop")]),t._v(" layer that always returns true, or the "),s("code",[t._v("PodsHealth.local")]),t._v(" layer that uses "),s("code",[t._v("ping")]),t._v(" from the "),s("a",{attrs:{href:"#messaging-protocol"}},[t._v("Messaging Protocol")]),t._v(" to check if a pod is alive.")]),t._v(" "),s("p",[t._v("Shardcake provides an implementation of "),s("code",[t._v("PodsHealth")]),t._v(" using the "),s("a",{attrs:{href:"https://kubernetes.io",target:"_blank",rel:"noopener noreferrer"}},[t._v("Kubernetes"),s("OutboundLink")],1),t._v(" API. To use it, add the following dependency:")]),t._v(" "),s("div",{staticClass:"language-scala extra-class"},[s("pre",{pre:!0,attrs:{class:"language-scala"}},[s("code",[t._v("libraryDependencies "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"com.devsisters"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shardcake-health-k8s"')]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2.1.0"')]),t._v("\n")])])]),s("p",[t._v("You can then simply use the "),s("code",[t._v("K8sPodsHealth.live")]),t._v(" layer. This is requiring a "),s("code",[t._v("Pods")]),t._v(" layer that comes from "),s("a",{attrs:{href:"https://coralogix.github.io/zio-k8s/docs/overview/overview_gettingstarted",target:"_blank",rel:"noopener noreferrer"}},[t._v("zio-k8s"),s("OutboundLink")],1),t._v(".")]),t._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[t._v("Examples")]),t._v(" "),s("p",[t._v("Check the "),s("a",{attrs:{href:"https://github.com/devsisters/shardcake/tree/series/2.x/examples/src/main/scala/example/complex",target:"_blank",rel:"noopener noreferrer"}},[t._v("examples"),s("OutboundLink")],1),t._v(" folder that contains a full example using Redis, gRPC and Kryo seralization.")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/3.bc6bba9e.js b/assets/js/3.bc6bba9e.js new file mode 100644 index 0000000..fa29bc7 --- /dev/null +++ b/assets/js/3.bc6bba9e.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[3,18,19],{239:function(t,e,n){"use strict";n.d(e,"d",(function(){return r})),n.d(e,"a",(function(){return s})),n.d(e,"i",(function(){return a})),n.d(e,"f",(function(){return l})),n.d(e,"g",(function(){return u})),n.d(e,"h",(function(){return c})),n.d(e,"b",(function(){return p})),n.d(e,"e",(function(){return d})),n.d(e,"k",(function(){return h})),n.d(e,"l",(function(){return f})),n.d(e,"c",(function(){return g})),n.d(e,"j",(function(){return m}));n(90);const r=/#.*$/,i=/\.(md|html)$/,s=/\/$/,a=/^[a-z]+:/i;function o(t){return decodeURI(t).replace(r,"").replace(i,"")}function l(t){return a.test(t)}function u(t){return/^mailto:/.test(t)}function c(t){return/^tel:/.test(t)}function p(t){if(l(t))return t;const e=t.match(r),n=e?e[0]:"",i=o(t);return s.test(i)?t:i+".html"+n}function d(t,e){const n=decodeURIComponent(t.hash),i=function(t){const e=t.match(r);if(e)return e[0]}(e);if(i&&n!==i)return!1;return o(t.path)===o(e)}function h(t,e,n){if(l(e))return{type:"external",path:e};n&&(e=function(t,e,n){const r=t.charAt(0);if("/"===r)return t;if("?"===r||"#"===r)return e+t;const i=e.split("/");n&&i[i.length-1]||i.pop();const s=t.replace(/^\//,"").split("/");for(let t=0;tfunction t(e,n,r,i=1){if("string"==typeof e)return h(n,e,r);if(Array.isArray(e))return Object.assign(h(n,e[0],r),{title:e[1]});{const s=e.children||[];return 0===s.length&&e.path?Object.assign(h(n,e.path,r),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:s.map(e=>t(e,n,r,i+1)),collapsable:!1!==e.collapsable}}}(t,i,n)):[]}return[]}function b(t){const e=g(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:e.map(e=>({type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}))}]}function g(t){let e;return(t=t.map(t=>Object.assign({},t))).forEach(t=>{2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)}),t.filter(t=>2===t.level)}function m(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},240:function(t,e,n){},242:function(t,e,n){"use strict";n.r(e);var r={name:"DropdownTransition",methods:{setHeight(t){t.style.height=t.scrollHeight+"px"},unsetHeight(t){t.style.height=""}}},i=(n(243),n(14)),s=Object(i.a)(r,(function(){return(0,this._self._c)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.default=s.exports},243:function(t,e,n){"use strict";n(240)},245:function(t,e,n){},250:function(t,e,n){},252:function(t,e,n){"use strict";n(245)},253:function(t,e,n){"use strict";n.r(e);var r=n(266),i=n(255),s=n(239);function a(t,e){if("group"===e.type){const n=e.path&&Object(s.e)(t,e.path),r=e.children.some(e=>"group"===e.type?a(t,e):"page"===e.type&&Object(s.e)(t,e.path));return n||r}return!1}var o={name:"SidebarLinks",components:{SidebarGroup:r.default,SidebarLink:i.default},props:["items","depth","sidebarDepth","initialOpenGroupIndex"],data(){return{openGroupIndex:this.initialOpenGroupIndex||0}},watch:{$route(){this.refreshIndex()}},created(){this.refreshIndex()},methods:{refreshIndex(){const t=function(t,e){for(let n=0;n-1&&(this.openGroupIndex=t)},toggleGroup(t){this.openGroupIndex=t===this.openGroupIndex?-1:t},isActive(t){return Object(s.e)(this.$route,t.regularPath)}}},l=n(14),u=Object(l.a)(o,(function(){var t=this,e=t._self._c;return t.items.length?e("ul",{staticClass:"sidebar-links"},t._l(t.items,(function(n,r){return e("li",{key:r},["group"===n.type?e("SidebarGroup",{attrs:{item:n,open:r===t.openGroupIndex,collapsable:n.collapsable||n.collapsible,depth:t.depth},on:{toggle:function(e){return t.toggleGroup(r)}}}):e("SidebarLink",{attrs:{"sidebar-depth":t.sidebarDepth,item:n}})],1)})),0):t._e()}),[],!1,null,null,null);e.default=u.exports},255:function(t,e,n){"use strict";n.r(e);var r=n(239);function i(t,e,n,r,i){const s={props:{to:e,activeClass:"",exactActiveClass:""},class:{active:r,"sidebar-link":!0}};return i>2&&(s.style={"padding-left":i+"rem"}),t("RouterLink",s,n)}function s(t,e,n,a,o,l=1){return!e||l>o?null:t("ul",{class:"sidebar-sub-headers"},e.map(e=>{const u=Object(r.e)(a,n+"#"+e.slug);return t("li",{class:"sidebar-sub-header"},[i(t,n+"#"+e.slug,e.title,u,e.level-1),s(t,e.children,n,a,o,l+1)])}))}var a={functional:!0,props:["item","sidebarDepth"],render(t,{parent:{$page:e,$site:n,$route:a,$themeConfig:o,$themeLocaleConfig:l},props:{item:u,sidebarDepth:c}}){const p=Object(r.e)(a,u.path),d="auto"===u.type?p||u.children.some(t=>Object(r.e)(a,u.basePath+"#"+t.slug)):p,h="external"===u.type?function(t,e,n){return t("a",{attrs:{href:e,target:"_blank",rel:"noopener noreferrer"},class:{"sidebar-link":!0}},[n,t("OutboundLink")])}(t,u.path,u.title||u.path):i(t,u.path,u.title||u.path,d),f=[e.frontmatter.sidebarDepth,c,l.sidebarDepth,o.sidebarDepth,1].find(t=>void 0!==t),b=l.displayAllHeaders||o.displayAllHeaders;if("auto"===u.type)return[h,s(t,u.children,u.basePath,a,f)];if((d||b)&&u.headers&&!r.d.test(u.path)){return[h,s(t,Object(r.c)(u.headers),u.path,a,f)]}return h}},o=(n(252),n(14)),l=Object(o.a)(a,void 0,void 0,!1,null,null,null);e.default=l.exports},263:function(t,e,n){"use strict";n(250)},266:function(t,e,n){"use strict";n.r(e);var r=n(239),i={name:"SidebarGroup",components:{DropdownTransition:n(242).default},props:["item","open","collapsable","depth"],beforeCreate(){this.$options.components.SidebarLinks=n(253).default},methods:{isActive:r.e}},s=(n(263),n(14)),a=Object(s.a)(i,(function(){var t=this,e=t._self._c;return e("section",{staticClass:"sidebar-group",class:[{collapsable:t.collapsable,"is-sub-group":0!==t.depth},"depth-"+t.depth]},[t.item.path?e("RouterLink",{staticClass:"sidebar-heading clickable",class:{open:t.open,active:t.isActive(t.$route,t.item.path)},attrs:{to:t.item.path},nativeOn:{click:function(e){return t.$emit("toggle")}}},[e("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?e("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]):e("p",{staticClass:"sidebar-heading",class:{open:t.open},on:{click:function(e){return t.$emit("toggle")}}},[e("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?e("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]),t._v(" "),e("DropdownTransition",[t.open||!t.collapsable?e("SidebarLinks",{staticClass:"sidebar-group-items",attrs:{items:t.item.children,"sidebar-depth":t.item.sidebarDepth,"initial-open-group-index":t.item.initialOpenGroupIndex,depth:t.depth+1}}):t._e()],1)],1)}),[],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/4.45665f8a.js b/assets/js/4.45665f8a.js new file mode 100644 index 0000000..0586a17 --- /dev/null +++ b/assets/js/4.45665f8a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{277:function(t,e,n){},291:function(t,e,n){"use strict";n(277)},302:function(t,e,n){"use strict";n.r(e);var i={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:(t,{props:e,slots:n})=>t("span",{class:["badge",e.type],style:{verticalAlign:e.vertical}},e.text||n().default)},p=(n(291),n(14)),l=Object(p.a)(i,void 0,void 0,!1,null,"15b7b770",null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/5.7098d77a.js b/assets/js/5.7098d77a.js new file mode 100644 index 0000000..6a24bae --- /dev/null +++ b/assets/js/5.7098d77a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{278:function(t,e,a){},292:function(t,e,a){"use strict";a(278)},298:function(t,e,a){"use strict";a.r(e);var s={name:"CodeBlock",props:{title:{type:String,required:!0},active:{type:Boolean,default:!1}},mounted(){this.$parent&&this.$parent.loadTabs&&this.$parent.loadTabs()}},i=(a(292),a(14)),n=Object(i.a)(s,(function(){return(0,this._self._c)("div",{staticClass:"theme-code-block",class:{"theme-code-block__active":this.active}},[this._t("default")],2)}),[],!1,null,"759a7d02",null);e.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/6.0c0a0f39.js b/assets/js/6.0c0a0f39.js new file mode 100644 index 0000000..9d63aa1 --- /dev/null +++ b/assets/js/6.0c0a0f39.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{279:function(e,t,a){},293:function(e,t,a){"use strict";a(279)},299:function(e,t,a){"use strict";a.r(t);var o={name:"CodeGroup",data:()=>({codeTabs:[],activeCodeTabIndex:-1}),watch:{activeCodeTabIndex(e){this.activateCodeTab(e)}},mounted(){this.loadTabs()},methods:{changeCodeTab(e){this.activeCodeTabIndex=e},loadTabs(){this.codeTabs=(this.$slots.default||[]).filter(e=>Boolean(e.componentOptions)).map((e,t)=>(""===e.componentOptions.propsData.active&&(this.activeCodeTabIndex=t),{title:e.componentOptions.propsData.title,elm:e.elm})),-1===this.activeCodeTabIndex&&this.codeTabs.length>0&&(this.activeCodeTabIndex=0),this.activateCodeTab(0)},activateCodeTab(e){this.codeTabs.forEach(e=>{e.elm&&e.elm.classList.remove("theme-code-block__active")}),this.codeTabs[e].elm&&this.codeTabs[e].elm.classList.add("theme-code-block__active")}}},s=(a(293),a(14)),c=Object(s.a)(o,(function(){var e=this,t=e._self._c;return t("ClientOnly",[t("div",{staticClass:"theme-code-group"},[t("div",{staticClass:"theme-code-group__nav"},[t("ul",{staticClass:"theme-code-group__ul"},e._l(e.codeTabs,(function(a,o){return t("li",{key:a.title,staticClass:"theme-code-group__li"},[t("button",{staticClass:"theme-code-group__nav-tab",class:{"theme-code-group__nav-tab-active":o===e.activeCodeTabIndex},on:{click:function(t){return e.changeCodeTab(o)}}},[e._v("\n "+e._s(a.title)+"\n ")])])})),0)]),e._v(" "),e._t("default"),e._v(" "),e.codeTabs.length<1?t("pre",{staticClass:"pre-blank"},[e._v("// Make sure to add code blocks to your code group")]):e._e()],2)])}),[],!1,null,"deefee04",null);t.default=c.exports}}]); \ No newline at end of file diff --git a/assets/js/7.6a854e57.js b/assets/js/7.6a854e57.js new file mode 100644 index 0000000..fad0db4 --- /dev/null +++ b/assets/js/7.6a854e57.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{300:function(t,e,s){"use strict";s.r(e);const o=["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."];var n={methods:{getMsg:()=>o[Math.floor(Math.random()*o.length)]}},h=s(14),i=Object(h.a)(n,(function(){var t=this._self._c;return t("div",{staticClass:"theme-container"},[t("div",{staticClass:"theme-default-content"},[t("h1",[this._v("404")]),this._v(" "),t("blockquote",[this._v(this._s(this.getMsg()))]),this._v(" "),t("RouterLink",{attrs:{to:"/"}},[this._v("\n Take me home.\n ")])],1)])}),[],!1,null,null,null);e.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/app.4a6ace25.js b/assets/js/app.4a6ace25.js new file mode 100644 index 0000000..971e786 --- /dev/null +++ b/assets/js/app.4a6ace25.js @@ -0,0 +1,16 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[0],[]]);!function(t){function e(e){for(var r,a,s=e[0],c=e[1],u=e[2],l=0,p=[];l
'};function o(t,e,n){return tn?n:t}function i(t){return 100*(-1+t)}n.configure=function(t){var e,n;for(e in t)void 0!==(n=t[e])&&t.hasOwnProperty(e)&&(r[e]=n);return this},n.status=null,n.set=function(t){var e=n.isStarted();t=o(t,r.minimum,1),n.status=1===t?null:t;var c=n.render(!e),u=c.querySelector(r.barSelector),f=r.speed,l=r.easing;return c.offsetWidth,a((function(e){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),s(u,function(t,e,n){var o;return(o="translate3d"===r.positionUsing?{transform:"translate3d("+i(t)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+i(t)+"%,0)"}:{"margin-left":i(t)+"%"}).transition="all "+e+"ms "+n,o}(t,f,l)),1===t?(s(c,{transition:"none",opacity:1}),c.offsetWidth,setTimeout((function(){s(c,{transition:"all "+f+"ms linear",opacity:0}),setTimeout((function(){n.remove(),e()}),f)}),f)):setTimeout(e,f)})),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var t=function(){setTimeout((function(){n.status&&(n.trickle(),t())}),r.trickleSpeed)};return r.trickle&&t(),this},n.done=function(t){return t||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(t){var e=n.status;return e?("number"!=typeof t&&(t=(1-e)*o(Math.random()*e,.1,.95)),e=o(e+t,0,.994),n.set(e)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},t=0,e=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===e&&n.start(),t++,e++,r.always((function(){0==--e?(t=0,n.done()):n.set((t-e)/t)})),this):this},n.render=function(t){if(n.isRendered())return document.getElementById("nprogress");u(document.documentElement,"nprogress-busy");var e=document.createElement("div");e.id="nprogress",e.innerHTML=r.template;var o,a=e.querySelector(r.barSelector),c=t?"-100":i(n.status||0),f=document.querySelector(r.parent);return s(a,{transition:"all 0 linear",transform:"translate3d("+c+"%,0,0)"}),r.showSpinner||(o=e.querySelector(r.spinnerSelector))&&p(o),f!=document.body&&u(f,"nprogress-custom-parent"),f.appendChild(e),e},n.remove=function(){f(document.documentElement,"nprogress-busy"),f(document.querySelector(r.parent),"nprogress-custom-parent");var t=document.getElementById("nprogress");t&&p(t)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var t=document.body.style,e="WebkitTransform"in t?"Webkit":"MozTransform"in t?"Moz":"msTransform"in t?"ms":"OTransform"in t?"O":"";return e+"Perspective"in t?"translate3d":e+"Transform"in t?"translate":"margin"};var a=function(){var t=[];function e(){var n=t.shift();n&&n(e)}return function(n){t.push(n),1==t.length&&e()}}(),s=function(){var t=["Webkit","O","Moz","ms"],e={};function n(n){return n=n.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,(function(t,e){return e.toUpperCase()})),e[n]||(e[n]=function(e){var n=document.body.style;if(e in n)return e;for(var r,o=t.length,i=e.charAt(0).toUpperCase()+e.slice(1);o--;)if((r=t[o]+i)in n)return r;return e}(n))}function r(t,e,r){e=n(e),t.style[e]=r}return function(t,e){var n,o,i=arguments;if(2==i.length)for(n in e)void 0!==(o=e[n])&&e.hasOwnProperty(n)&&r(t,n,o);else r(t,i[1],i[2])}}();function c(t,e){return("string"==typeof t?t:l(t)).indexOf(" "+e+" ")>=0}function u(t,e){var n=l(t),r=n+e;c(n,e)||(t.className=r.substring(1))}function f(t,e){var n,r=l(t);c(t,e)&&(n=r.replace(" "+e+" "," "),t.className=n.substring(1,n.length-1))}function l(t){return(" "+(t.className||"")+" ").replace(/\s+/gi," ")}function p(t){t&&t.parentNode&&t.parentNode.removeChild(t)}return n})?r.call(e,n,e,t):r)||(t.exports=o)},function(t,e,n){"use strict";var r=n(1),o=n(45).f,i=n(12),a=n(92),s=n(32),c=n(61),u=n(120);t.exports=function(t,e){var n,f,l,p,d,h=t.target,v=t.global,m=t.stat;if(n=v?r:m?r[h]||s(h,{}):(r[h]||{}).prototype)for(f in e){if(p=e[f],l=t.dontCallGetSet?(d=o(n,f))&&d.value:n[f],!u(v?f:h+(m?".":"#")+f,t.forced)&&void 0!==l){if(typeof p==typeof l)continue;c(p,l)}(t.sham||l&&l.sham)&&i(p,"sham",!0),a(n,f,p,t)}}},function(t,e,n){"use strict";var r=n(25),o=Function.prototype.call;t.exports=r?o.bind(o):function(){return o.apply(o,arguments)}},function(t,e,n){"use strict";var r=n(3);t.exports=!r((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))},function(t,e,n){"use strict";t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e,n){"use strict";var r=n(46),o=n(47);t.exports=function(t){return r(o(t))}},function(t,e,n){"use strict";var r=n(1),o=n(0),i=function(t){return o(t)?t:void 0};t.exports=function(t,e){return arguments.length<2?i(r[t]):r[t]&&r[t][e]}},function(t,e,n){"use strict";var r=n(0),o=n(102),i=TypeError;t.exports=function(t){if(r(t))return t;throw new i(o(t)+" is not a function")}},function(t,e,n){"use strict";var r=n(1),o=n(56),i=n(7),a=n(58),s=n(54),c=n(53),u=r.Symbol,f=o("wks"),l=c?u.for||u:u&&u.withoutSetter||a;t.exports=function(t){return i(f,t)||(f[t]=s&&i(u,t)?u[t]:l("Symbol."+t)),f[t]}},function(t,e,n){"use strict";var r=n(1),o=n(32),i=r["__core-js_shared__"]||o("__core-js_shared__",{});t.exports=i},function(t,e,n){"use strict";var r=n(1),o=Object.defineProperty;t.exports=function(t,e){try{o(r,t,{value:e,configurable:!0,writable:!0})}catch(n){r[t]=e}return e}},function(t,e,n){"use strict";var r=n(47),o=Object;t.exports=function(t){return o(r(t))}},function(t,e,n){"use strict";var r=n(8),o=String,i=TypeError;t.exports=function(t){if(r(t))return t;throw new i(o(t)+" is not an object")}},function(t,e,n){"use strict";var r=n(117);t.exports=function(t){return r(t.length)}},function(t,e,n){var r=n(143),o=n(10),i=Object.prototype,a=i.hasOwnProperty,s=i.propertyIsEnumerable,c=r(function(){return arguments}())?r:function(t){return o(t)&&a.call(t,"callee")&&!s.call(t,"callee")};t.exports=c},function(t,e,n){var r=n(9)(n(6),"Map");t.exports=r},function(t,e){t.exports=function(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}},function(t,e,n){var r=n(163),o=n(170),i=n(172),a=n(173),s=n(174);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e-1&&t%1==0&&t<=9007199254740991}},function(t,e,n){var r=n(4),o=n(43),i=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,a=/^\w*$/;t.exports=function(t,e){if(r(t))return!1;var n=typeof t;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=t&&!o(t))||(a.test(t)||!i.test(t)||null!=e&&t in Object(e))}},function(t,e,n){var r=n(11),o=n(10);t.exports=function(t){return"symbol"==typeof t||o(t)&&"[object Symbol]"==r(t)}},function(t,e){t.exports=function(t){return t}},function(t,e,n){"use strict";var r=n(5),o=n(24),i=n(98),a=n(26),s=n(27),c=n(49),u=n(7),f=n(59),l=Object.getOwnPropertyDescriptor;e.f=r?l:function(t,e){if(t=s(t),e=c(e),f)try{return l(t,e)}catch(t){}if(u(t,e))return a(!o(i.f,t,e),t[e])}},function(t,e,n){"use strict";var r=n(2),o=n(3),i=n(16),a=Object,s=r("".split);t.exports=o((function(){return!a("z").propertyIsEnumerable(0)}))?function(t){return"String"===i(t)?s(t,""):a(t)}:a},function(t,e,n){"use strict";var r=n(48),o=TypeError;t.exports=function(t){if(r(t))throw new o("Can't call method on "+t);return t}},function(t,e,n){"use strict";t.exports=function(t){return null==t}},function(t,e,n){"use strict";var r=n(99),o=n(51);t.exports=function(t){var e=r(t,"string");return o(e)?e:e+""}},function(t,e,n){"use strict";var r="object"==typeof document&&document.all,o=void 0===r&&void 0!==r;t.exports={all:r,IS_HTMLDDA:o}},function(t,e,n){"use strict";var r=n(28),o=n(0),i=n(52),a=n(53),s=Object;t.exports=a?function(t){return"symbol"==typeof t}:function(t){var e=r("Symbol");return o(e)&&i(e.prototype,s(t))}},function(t,e,n){"use strict";var r=n(2);t.exports=r({}.isPrototypeOf)},function(t,e,n){"use strict";var r=n(54);t.exports=r&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},function(t,e,n){"use strict";var r=n(55),o=n(3),i=n(1).String;t.exports=!!Object.getOwnPropertySymbols&&!o((function(){var t=Symbol("symbol detection");return!i(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&r&&r<41}))},function(t,e,n){"use strict";var r,o,i=n(1),a=n(100),s=i.process,c=i.Deno,u=s&&s.versions||c&&c.version,f=u&&u.v8;f&&(o=(r=f.split("."))[0]>0&&r[0]<4?1:+(r[0]+r[1])),!o&&a&&(!(r=a.match(/Edge\/(\d+)/))||r[1]>=74)&&(r=a.match(/Chrome\/(\d+)/))&&(o=+r[1]),t.exports=o},function(t,e,n){"use strict";var r=n(57),o=n(31);(t.exports=function(t,e){return o[t]||(o[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.33.2",mode:r?"pure":"global",copyright:"© 2014-2023 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.33.2/LICENSE",source:"https://github.com/zloirock/core-js"})},function(t,e,n){"use strict";t.exports=!1},function(t,e,n){"use strict";var r=n(2),o=0,i=Math.random(),a=r(1..toString);t.exports=function(t){return"Symbol("+(void 0===t?"":t)+")_"+a(++o+i,36)}},function(t,e,n){"use strict";var r=n(5),o=n(3),i=n(104);t.exports=!r&&!o((function(){return 7!==Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},function(t,e,n){"use strict";t.exports={}},function(t,e,n){"use strict";var r=n(7),o=n(111),i=n(45),a=n(15);t.exports=function(t,e,n){for(var s=o(e),c=a.f,u=i.f,f=0;ff))return!1;var p=c.get(t),d=c.get(e);if(p&&d)return p==e&&d==t;var h=-1,v=!0,m=2&n?new r:void 0;for(c.set(t,e),c.set(e,t);++h-1&&t%1==0&&t]/;t.exports=function(t){var e,n=""+t,o=r.exec(n);if(!o)return n;var i="",a=0,s=0;for(a=o.index;a=e||n<0||m&&t-u>=i}function x(){var t=d();if(_(t))return w(t);s=setTimeout(x,function(t){var n=e-(t-c);return m?p(n,i-(t-u)):n}(t))}function w(t){return s=void 0,y&&r?g(t):(r=o=void 0,a)}function O(){var t=d(),n=_(t);if(r=arguments,o=this,c=t,n){if(void 0===s)return b(c);if(m)return s=setTimeout(x,e),g(c)}return void 0===s&&(s=setTimeout(x,e)),a}return e=v(e)||0,h(n)&&(f=!!n.leading,i=(m="maxWait"in n)?l(v(n.maxWait)||0,e):i,y="trailing"in n?!!n.trailing:y),O.cancel=function(){void 0!==s&&clearTimeout(s),u=0,r=c=o=s=void 0},O.flush=function(){return void 0===s?a:w(d())},O}},function(t,e,n){"use strict";var r=n(23),o=n(33),i=n(35),a=n(124),s=n(126);r({target:"Array",proto:!0,arity:1,forced:n(3)((function(){return 4294967297!==[].push.call({length:4294967296},1)}))||!function(){try{Object.defineProperty([],"length",{writable:!1}).push()}catch(t){return t instanceof TypeError}}()},{push:function(t){var e=o(this),n=i(e),r=arguments.length;s(n+r);for(var c=0;c79&&a<83||!i("reduce")},{reduce:function(t){var e=arguments.length;return o(this,t,e,e>1?arguments[1]:void 0)}})},function(t,e,n){"use strict";var r={}.propertyIsEnumerable,o=Object.getOwnPropertyDescriptor,i=o&&!r.call({1:2},1);e.f=i?function(t){var e=o(this,t);return!!e&&e.enumerable}:r},function(t,e,n){"use strict";var r=n(24),o=n(8),i=n(51),a=n(101),s=n(103),c=n(30),u=TypeError,f=c("toPrimitive");t.exports=function(t,e){if(!o(t)||i(t))return t;var n,c=a(t,f);if(c){if(void 0===e&&(e="default"),n=r(c,t,e),!o(n)||i(n))return n;throw new u("Can't convert object to primitive value")}return void 0===e&&(e="number"),s(t,e)}},function(t,e,n){"use strict";t.exports="undefined"!=typeof navigator&&String(navigator.userAgent)||""},function(t,e,n){"use strict";var r=n(29),o=n(48);t.exports=function(t,e){var n=t[e];return o(n)?void 0:r(n)}},function(t,e,n){"use strict";var r=String;t.exports=function(t){try{return r(t)}catch(t){return"Object"}}},function(t,e,n){"use strict";var r=n(24),o=n(0),i=n(8),a=TypeError;t.exports=function(t,e){var n,s;if("string"===e&&o(n=t.toString)&&!i(s=r(n,t)))return s;if(o(n=t.valueOf)&&!i(s=r(n,t)))return s;if("string"!==e&&o(n=t.toString)&&!i(s=r(n,t)))return s;throw new a("Can't convert object to primitive value")}},function(t,e,n){"use strict";var r=n(1),o=n(8),i=r.document,a=o(i)&&o(i.createElement);t.exports=function(t){return a?i.createElement(t):{}}},function(t,e,n){"use strict";var r=n(5),o=n(3);t.exports=r&&o((function(){return 42!==Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype}))},function(t,e,n){"use strict";var r=n(5),o=n(7),i=Function.prototype,a=r&&Object.getOwnPropertyDescriptor,s=o(i,"name"),c=s&&"something"===function(){}.name,u=s&&(!r||r&&a(i,"name").configurable);t.exports={EXISTS:s,PROPER:c,CONFIGURABLE:u}},function(t,e,n){"use strict";var r=n(2),o=n(0),i=n(31),a=r(Function.toString);o(i.inspectSource)||(i.inspectSource=function(t){return a(t)}),t.exports=i.inspectSource},function(t,e,n){"use strict";var r,o,i,a=n(109),s=n(1),c=n(8),u=n(12),f=n(7),l=n(31),p=n(110),d=n(60),h=s.TypeError,v=s.WeakMap;if(a||l.state){var m=l.state||(l.state=new v);m.get=m.get,m.has=m.has,m.set=m.set,r=function(t,e){if(m.has(t))throw new h("Object already initialized");return e.facade=t,m.set(t,e),e},o=function(t){return m.get(t)||{}},i=function(t){return m.has(t)}}else{var y=p("state");d[y]=!0,r=function(t,e){if(f(t,y))throw new h("Object already initialized");return e.facade=t,u(t,y,e),e},o=function(t){return f(t,y)?t[y]:{}},i=function(t){return f(t,y)}}t.exports={set:r,get:o,has:i,enforce:function(t){return i(t)?o(t):r(t,{})},getterFor:function(t){return function(e){var n;if(!c(e)||(n=o(e)).type!==t)throw new h("Incompatible receiver, "+t+" required");return n}}}},function(t,e,n){"use strict";var r=n(1),o=n(0),i=r.WeakMap;t.exports=o(i)&&/native code/.test(String(i))},function(t,e,n){"use strict";var r=n(56),o=n(58),i=r("keys");t.exports=function(t){return i[t]||(i[t]=o(t))}},function(t,e,n){"use strict";var r=n(28),o=n(2),i=n(112),a=n(119),s=n(34),c=o([].concat);t.exports=r("Reflect","ownKeys")||function(t){var e=i.f(s(t)),n=a.f;return n?c(e,n(t)):e}},function(t,e,n){"use strict";var r=n(113),o=n(118).concat("length","prototype");e.f=Object.getOwnPropertyNames||function(t){return r(t,o)}},function(t,e,n){"use strict";var r=n(2),o=n(7),i=n(27),a=n(114).indexOf,s=n(60),c=r([].push);t.exports=function(t,e){var n,r=i(t),u=0,f=[];for(n in r)!o(s,n)&&o(r,n)&&c(f,n);for(;e.length>u;)o(r,n=e[u++])&&(~a(f,n)||c(f,n));return f}},function(t,e,n){"use strict";var r=n(27),o=n(115),i=n(35),a=function(t){return function(e,n,a){var s,c=r(e),u=i(c),f=o(a,u);if(t&&n!=n){for(;u>f;)if((s=c[f++])!=s)return!0}else for(;u>f;f++)if((t||f in c)&&c[f]===n)return t||f||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},function(t,e,n){"use strict";var r=n(62),o=Math.max,i=Math.min;t.exports=function(t,e){var n=r(t);return n<0?o(n+e,0):i(n,e)}},function(t,e,n){"use strict";var r=Math.ceil,o=Math.floor;t.exports=Math.trunc||function(t){var e=+t;return(e>0?o:r)(e)}},function(t,e,n){"use strict";var r=n(62),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},function(t,e,n){"use strict";t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},function(t,e,n){"use strict";e.f=Object.getOwnPropertySymbols},function(t,e,n){"use strict";var r=n(3),o=n(0),i=/#|\.prototype\./,a=function(t,e){var n=c[s(t)];return n===f||n!==u&&(o(e)?r(e):!!e)},s=a.normalize=function(t){return String(t).replace(i,".").toLowerCase()},c=a.data={},u=a.NATIVE="N",f=a.POLYFILL="P";t.exports=a},function(t,e,n){"use strict";var r=n(29),o=n(33),i=n(46),a=n(35),s=TypeError,c=function(t){return function(e,n,c,u){r(n);var f=o(e),l=i(f),p=a(f),d=t?p-1:0,h=t?-1:1;if(c<2)for(;;){if(d in l){u=l[d],d+=h;break}if(d+=h,t?d<0:p<=d)throw new s("Reduce of empty array with no initial value")}for(;t?d>=0:p>d;d+=h)d in l&&(u=n(u,l[d],d,f));return u}};t.exports={left:c(!1),right:c(!0)}},function(t,e,n){"use strict";var r=n(3);t.exports=function(t,e){var n=[][t];return!!n&&r((function(){n.call(null,e||function(){return 1},1)}))}},function(t,e,n){"use strict";var r=n(1),o=n(16);t.exports="process"===o(r.process)},function(t,e,n){"use strict";var r=n(5),o=n(125),i=TypeError,a=Object.getOwnPropertyDescriptor,s=r&&!function(){if(void 0!==this)return!0;try{Object.defineProperty([],"length",{writable:!1}).length=1}catch(t){return t instanceof TypeError}}();t.exports=s?function(t,e){if(o(t)&&!a(t,"length").writable)throw new i("Cannot set read only .length");return t.length=e}:function(t,e){return t.length=e}},function(t,e,n){"use strict";var r=n(16);t.exports=Array.isArray||function(t){return"Array"===r(t)}},function(t,e,n){"use strict";var r=TypeError;t.exports=function(t){if(t>9007199254740991)throw r("Maximum allowed index exceeded");return t}},function(t,e,n){"use strict";var r=n(23),o=n(1),i=n(128),a=n(129),s=o.WebAssembly,c=7!==new Error("e",{cause:7}).cause,u=function(t,e){var n={};n[t]=a(t,e,c),r({global:!0,constructor:!0,arity:1,forced:c},n)},f=function(t,e){if(s&&s[t]){var n={};n[t]=a("WebAssembly."+t,e,c),r({target:"WebAssembly",stat:!0,constructor:!0,arity:1,forced:c},n)}};u("Error",(function(t){return function(e){return i(t,this,arguments)}})),u("EvalError",(function(t){return function(e){return i(t,this,arguments)}})),u("RangeError",(function(t){return function(e){return i(t,this,arguments)}})),u("ReferenceError",(function(t){return function(e){return i(t,this,arguments)}})),u("SyntaxError",(function(t){return function(e){return i(t,this,arguments)}})),u("TypeError",(function(t){return function(e){return i(t,this,arguments)}})),u("URIError",(function(t){return function(e){return i(t,this,arguments)}})),f("CompileError",(function(t){return function(e){return i(t,this,arguments)}})),f("LinkError",(function(t){return function(e){return i(t,this,arguments)}})),f("RuntimeError",(function(t){return function(e){return i(t,this,arguments)}}))},function(t,e,n){"use strict";var r=n(25),o=Function.prototype,i=o.apply,a=o.call;t.exports="object"==typeof Reflect&&Reflect.apply||(r?a.bind(i):function(){return a.apply(i,arguments)})},function(t,e,n){"use strict";var r=n(28),o=n(7),i=n(12),a=n(52),s=n(63),c=n(61),u=n(132),f=n(133),l=n(134),p=n(137),d=n(138),h=n(5),v=n(57);t.exports=function(t,e,n,m){var y=m?2:1,g=t.split("."),b=g[g.length-1],_=r.apply(null,g);if(_){var x=_.prototype;if(!v&&o(x,"cause")&&delete x.cause,!n)return _;var w=r("Error"),O=e((function(t,e){var n=l(m?e:t,void 0),r=m?new _(t):new _;return void 0!==n&&i(r,"message",n),d(r,O,r.stack,2),this&&a(x,this)&&f(r,this,O),arguments.length>y&&p(r,arguments[y]),r}));if(O.prototype=x,"Error"!==b?s?s(O,w):c(O,w,{name:!0}):h&&"stackTraceLimit"in _&&(u(O,_,"stackTraceLimit"),u(O,_,"prepareStackTrace")),c(O,_),!v)try{x.name!==b&&i(x,"name",b),x.constructor=O}catch(t){}return O}}},function(t,e,n){"use strict";var r=n(2),o=n(29);t.exports=function(t,e,n){try{return r(o(Object.getOwnPropertyDescriptor(t,e)[n]))}catch(t){}}},function(t,e,n){"use strict";var r=n(0),o=String,i=TypeError;t.exports=function(t){if("object"==typeof t||r(t))return t;throw new i("Can't set "+o(t)+" as a prototype")}},function(t,e,n){"use strict";var r=n(15).f;t.exports=function(t,e,n){n in t||r(t,n,{configurable:!0,get:function(){return e[n]},set:function(t){e[n]=t}})}},function(t,e,n){"use strict";var r=n(0),o=n(8),i=n(63);t.exports=function(t,e,n){var a,s;return i&&r(a=e.constructor)&&a!==n&&o(s=a.prototype)&&s!==n.prototype&&i(t,s),t}},function(t,e,n){"use strict";var r=n(93);t.exports=function(t,e){return void 0===t?arguments.length<2?"":e:r(t)}},function(t,e,n){"use strict";var r=n(136),o=n(0),i=n(16),a=n(30)("toStringTag"),s=Object,c="Arguments"===i(function(){return arguments}());t.exports=r?i:function(t){var e,n,r;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=s(t),a))?n:c?i(e):"Object"===(r=i(e))&&o(e.callee)?"Arguments":r}},function(t,e,n){"use strict";var r={};r[n(30)("toStringTag")]="z",t.exports="[object z]"===String(r)},function(t,e,n){"use strict";var r=n(8),o=n(12);t.exports=function(t,e){r(e)&&"cause"in e&&o(t,"cause",e.cause)}},function(t,e,n){"use strict";var r=n(12),o=n(139),i=n(140),a=Error.captureStackTrace;t.exports=function(t,e,n,s){i&&(a?a(t,e):r(t,"stack",o(n,s)))}},function(t,e,n){"use strict";var r=n(2),o=Error,i=r("".replace),a=String(new o("zxcasd").stack),s=/\n\s*at [^:]*:[^\n]*/,c=s.test(a);t.exports=function(t,e){if(c&&"string"==typeof t&&!o.prepareStackTrace)for(;e--;)t=i(t,s,"");return t}},function(t,e,n){"use strict";var r=n(3),o=n(26);t.exports=!r((function(){var t=new Error("a");return!("stack"in t)||(Object.defineProperty(t,"stack",o(1,7)),7!==t.stack)}))},function(t,e,n){var r=n(64),o=n(142);t.exports=function t(e,n,i,a,s){var c=-1,u=e.length;for(i||(i=o),s||(s=[]);++c0&&i(f)?n>1?t(f,n-1,i,a,s):r(s,f):a||(s[s.length]=f)}return s}},function(t,e,n){var r=n(13),o=n(36),i=n(4),a=r?r.isConcatSpreadable:void 0;t.exports=function(t){return i(t)||o(t)||!!(a&&t&&t[a])}},function(t,e,n){var r=n(11),o=n(10);t.exports=function(t){return o(t)&&"[object Arguments]"==r(t)}},function(t,e,n){var r=n(13),o=Object.prototype,i=o.hasOwnProperty,a=o.toString,s=r?r.toStringTag:void 0;t.exports=function(t){var e=i.call(t,s),n=t[s];try{t[s]=void 0;var r=!0}catch(t){}var o=a.call(t);return r&&(e?t[s]=n:delete t[s]),o}},function(t,e){var n=Object.prototype.toString;t.exports=function(t){return n.call(t)}},function(t,e,n){var r=n(147),o=n(203),i=n(44),a=n(4),s=n(213);t.exports=function(t){return"function"==typeof t?t:null==t?i:"object"==typeof t?a(t)?o(t[0],t[1]):r(t):s(t)}},function(t,e,n){var r=n(148),o=n(202),i=n(82);t.exports=function(t){var e=o(t);return 1==e.length&&e[0][2]?i(e[0][0],e[0][1]):function(n){return n===t||r(n,t,e)}}},function(t,e,n){var r=n(66),o=n(70);t.exports=function(t,e,n,i){var a=n.length,s=a,c=!i;if(null==t)return!s;for(t=Object(t);a--;){var u=n[a];if(c&&u[2]?u[1]!==t[u[0]]:!(u[0]in t))return!1}for(;++a-1}},function(t,e,n){var r=n(18);t.exports=function(t,e){var n=this.__data__,o=r(n,t);return o<0?(++this.size,n.push([t,e])):n[o][1]=e,this}},function(t,e,n){var r=n(17);t.exports=function(){this.__data__=new r,this.size=0}},function(t,e){t.exports=function(t){var e=this.__data__,n=e.delete(t);return this.size=e.size,n}},function(t,e){t.exports=function(t){return this.__data__.get(t)}},function(t,e){t.exports=function(t){return this.__data__.has(t)}},function(t,e,n){var r=n(17),o=n(37),i=n(39);t.exports=function(t,e){var n=this.__data__;if(n instanceof r){var a=n.__data__;if(!o||a.length<199)return a.push([t,e]),this.size=++n.size,this;n=this.__data__=new i(a)}return n.set(t,e),this.size=n.size,this}},function(t,e,n){var r=n(68),o=n(160),i=n(38),a=n(69),s=/^\[object .+?Constructor\]$/,c=Function.prototype,u=Object.prototype,f=c.toString,l=u.hasOwnProperty,p=RegExp("^"+f.call(l).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=function(t){return!(!i(t)||o(t))&&(r(t)?p:s).test(a(t))}},function(t,e,n){var r,o=n(161),i=(r=/[^.]+$/.exec(o&&o.keys&&o.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"";t.exports=function(t){return!!i&&i in t}},function(t,e,n){var r=n(6)["__core-js_shared__"];t.exports=r},function(t,e){t.exports=function(t,e){return null==t?void 0:t[e]}},function(t,e,n){var r=n(164),o=n(17),i=n(37);t.exports=function(){this.size=0,this.__data__={hash:new r,map:new(i||o),string:new r}}},function(t,e,n){var r=n(165),o=n(166),i=n(167),a=n(168),s=n(169);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e0){if(++e>=800)return arguments[0]}else e=0;return t.apply(void 0,arguments)}}},function(t,e,n){var r=n(72),o=n(225),i=n(230),a=n(73),s=n(231),c=n(40);t.exports=function(t,e,n){var u=-1,f=o,l=t.length,p=!0,d=[],h=d;if(n)p=!1,f=i;else if(l>=200){var v=e?null:s(t);if(v)return c(v);p=!1,f=a,h=new r}else h=e?[]:d;t:for(;++u-1}},function(t,e,n){var r=n(227),o=n(228),i=n(229);t.exports=function(t,e,n){return e==e?i(t,e,n):r(t,o,n)}},function(t,e){t.exports=function(t,e,n,r){for(var o=t.length,i=n+(r?1:-1);r?i--:++i=0&&Math.floor(e)===e&&isFinite(t)}function v(t){return a(t)&&"function"==typeof t.then&&"function"==typeof t.catch}function m(t){return null==t?"":Array.isArray(t)||p(t)&&t.toString===l?JSON.stringify(t,null,2):String(t)}function y(t){var e=parseFloat(t);return isNaN(e)?t:e}function g(t,e){for(var n=Object.create(null),r=t.split(","),o=0;o-1)return t.splice(r,1)}}var x=Object.prototype.hasOwnProperty;function w(t,e){return x.call(t,e)}function O(t){var e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}var $=/-(\w)/g,C=O((function(t){return t.replace($,(function(t,e){return e?e.toUpperCase():""}))})),S=O((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),j=/\B([A-Z])/g,k=O((function(t){return t.replace(j,"-$1").toLowerCase()}));var E=Function.prototype.bind?function(t,e){return t.bind(e)}:function(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n};function P(t,e){e=e||0;for(var n=t.length-e,r=new Array(n);n--;)r[n]=t[n+e];return r}function A(t,e){for(var n in e)t[n]=e[n];return t}function T(t){for(var e={},n=0;n0,Z=J&&J.indexOf("edge/")>0;J&&J.indexOf("android");var Q=J&&/iphone|ipad|ipod|ios/.test(J);J&&/chrome\/\d+/.test(J),J&&/phantomjs/.test(J);var tt,et=J&&J.match(/firefox\/(\d+)/),nt={}.watch,rt=!1;if(G)try{var ot={};Object.defineProperty(ot,"passive",{get:function(){rt=!0}}),window.addEventListener("test-passive",null,ot)}catch(t){}var it=function(){return void 0===tt&&(tt=!G&&"undefined"!=typeof global&&(global.process&&"server"===global.process.env.VUE_ENV)),tt},at=G&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function st(t){return"function"==typeof t&&/native code/.test(t.toString())}var ct,ut="undefined"!=typeof Symbol&&st(Symbol)&&"undefined"!=typeof Reflect&&st(Reflect.ownKeys);ct="undefined"!=typeof Set&&st(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var ft=null;function lt(t){void 0===t&&(t=null),t||ft&&ft._scope.off(),ft=t,t&&t._scope.on()}var pt=function(){function t(t,e,n,r,o,i,a,s){this.tag=t,this.data=e,this.children=n,this.text=r,this.elm=o,this.ns=void 0,this.context=i,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=e&&e.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1}return Object.defineProperty(t.prototype,"child",{get:function(){return this.componentInstance},enumerable:!1,configurable:!0}),t}(),dt=function(t){void 0===t&&(t="");var e=new pt;return e.text=t,e.isComment=!0,e};function ht(t){return new pt(void 0,void 0,void 0,String(t))}function vt(t){var e=new pt(t.tag,t.data,t.children&&t.children.slice(),t.text,t.elm,t.context,t.componentOptions,t.asyncFactory);return e.ns=t.ns,e.isStatic=t.isStatic,e.key=t.key,e.isComment=t.isComment,e.fnContext=t.fnContext,e.fnOptions=t.fnOptions,e.fnScopeId=t.fnScopeId,e.asyncMeta=t.asyncMeta,e.isCloned=!0,e}var mt=0,yt=[],gt=function(){function t(){this._pending=!1,this.id=mt++,this.subs=[]}return t.prototype.addSub=function(t){this.subs.push(t)},t.prototype.removeSub=function(t){this.subs[this.subs.indexOf(t)]=null,this._pending||(this._pending=!0,yt.push(this))},t.prototype.depend=function(e){t.target&&t.target.addDep(this)},t.prototype.notify=function(t){var e=this.subs.filter((function(t){return t}));for(var n=0,r=e.length;n0&&(Jt((u=t(u,"".concat(n||"","_").concat(r)))[0])&&Jt(l)&&(p[f]=ht(l.text+u[0].text),u.shift()),p.push.apply(p,u)):c(u)?Jt(l)?p[f]=ht(l.text+u):""!==u&&p.push(ht(u)):Jt(u)&&Jt(l)?p[f]=ht(l.text+u.text):(s(e._isVList)&&a(u.tag)&&i(u.key)&&a(n)&&(u.key="__vlist".concat(n,"_").concat(r,"__")),p.push(u)));return p}(t):void 0}function Jt(t){return a(t)&&a(t.text)&&!1===t.isComment}function Xt(t,e){var n,r,i,s,c=null;if(o(t)||"string"==typeof t)for(c=new Array(t.length),n=0,r=t.length;n0,s=e?!!e.$stable:!a,c=e&&e.$key;if(e){if(e._normalized)return e._normalized;if(s&&o&&o!==r&&c===o.$key&&!a&&!o.$hasNormal)return o;for(var u in i={},e)e[u]&&"$"!==u[0]&&(i[u]=ve(t,n,u,e[u]))}else i={};for(var f in n)f in i||(i[f]=me(n,f));return e&&Object.isExtensible(e)&&(e._normalized=i),H(i,"$stable",s),H(i,"$key",c),H(i,"$hasNormal",a),i}function ve(t,e,n,r){var i=function(){var e=ft;lt(t);var n=arguments.length?r.apply(null,arguments):r({}),i=(n=n&&"object"==typeof n&&!o(n)?[n]:Gt(n))&&n[0];return lt(e),n&&(!i||1===n.length&&i.isComment&&!de(i))?void 0:n};return r.proxy&&Object.defineProperty(e,n,{get:i,enumerable:!0,configurable:!0}),i}function me(t,e){return function(){return t[e]}}function ye(t){return{get attrs(){if(!t._attrsProxy){var e=t._attrsProxy={};H(e,"_v_attr_proxy",!0),ge(e,t.$attrs,r,t,"$attrs")}return t._attrsProxy},get listeners(){t._listenersProxy||ge(t._listenersProxy={},t.$listeners,r,t,"$listeners");return t._listenersProxy},get slots(){return function(t){t._slotsProxy||_e(t._slotsProxy={},t.$scopedSlots);return t._slotsProxy}(t)},emit:E(t.$emit,t),expose:function(e){e&&Object.keys(e).forEach((function(n){return Ut(t,e,n)}))}}}function ge(t,e,n,r,o){var i=!1;for(var a in e)a in t?e[a]!==n[a]&&(i=!0):(i=!0,be(t,a,r,o));for(var a in t)a in e||(i=!0,delete t[a]);return i}function be(t,e,n,r){Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){return n[r][e]}})}function _e(t,e){for(var n in e)t[n]=e[n];for(var n in t)n in e||delete t[n]}var xe=null;function we(t,e){return(t.__esModule||ut&&"Module"===t[Symbol.toStringTag])&&(t=t.default),f(t)?e.extend(t):t}function Oe(t){if(o(t))for(var e=0;edocument.createEvent("Event").timeStamp&&(cn=function(){return un.now()})}var fn=function(t,e){if(t.post){if(!e.post)return 1}else if(e.post)return-1;return t.id-e.id};function ln(){var t,e;for(sn=cn(),on=!0,tn.sort(fn),an=0;anan&&tn[n].id>t.id;)n--;tn.splice(n+1,0,t)}else tn.push(t);rn||(rn=!0,Ne(ln))}}function dn(t,e){if(t){for(var n=Object.create(null),r=ut?Reflect.ownKeys(t):Object.keys(t),o=0;o-1)if(i&&!w(o,"default"))a=!1;else if(""===a||a===k(t)){var c=Mn(String,o.type);(c<0||s-1:"string"==typeof t?t.split(",").indexOf(e)>-1:!!d(t)&&t.test(e)}function Xn(t,e){var n=t.cache,r=t.keys,o=t._vnode;for(var i in n){var a=n[i];if(a){var s=a.name;s&&!e(s)&&Yn(n,i,r,o)}}}function Yn(t,e,n,r){var o=t[e];!o||r&&o.tag===r.tag||o.componentInstance.$destroy(),t[e]=null,_(n,e)}Wn.prototype._init=function(t){var e=this;e._uid=qn++,e._isVue=!0,e.__v_skip=!0,e._scope=new zt(!0),e._scope._vm=!0,t&&t._isComponent?function(t,e){var n=t.$options=Object.create(t.constructor.options),r=e._parentVnode;n.parent=e.parent,n._parentVnode=r;var o=r.componentOptions;n.propsData=o.propsData,n._parentListeners=o.listeners,n._renderChildren=o.children,n._componentTag=o.tag,e.render&&(n.render=e.render,n.staticRenderFns=e.staticRenderFns)}(e,t):e.$options=En(Hn(e.constructor),t||{},e),e._renderProxy=e,e._self=e,function(t){var e=t.$options,n=e.parent;if(n&&!e.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(t)}t.$parent=n,t.$root=n?n.$root:t,t.$children=[],t.$refs={},t._provided=n?n._provided:Object.create(null),t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}(e),function(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&Ge(t,e)}(e),function(t){t._vnode=null,t._staticTrees=null;var e=t.$options,n=t.$vnode=e._parentVnode,o=n&&n.context;t.$slots=le(e._renderChildren,o),t.$scopedSlots=n?he(t.$parent,n.data.scopedSlots,t.$slots):r,t._c=function(e,n,r,o){return $e(t,e,n,r,o,!1)},t.$createElement=function(e,n,r,o){return $e(t,e,n,r,o,!0)};var i=n&&n.data;At(t,"$attrs",i&&i.attrs||r,null,!0),At(t,"$listeners",e._parentListeners||r,null,!0)}(e),Qe(e,"beforeCreate",void 0,!1),function(t){var e=dn(t.$options.inject,t);e&&(jt(!1),Object.keys(e).forEach((function(n){At(t,n,e[n])})),jt(!0))}(e),Nn(e),function(t){var e=t.$options.provide;if(e){var n=u(e)?e.call(t):e;if(!f(n))return;for(var r=Bt(t),o=ut?Reflect.ownKeys(n):Object.keys(n),i=0;i1?P(n):n;for(var r=P(arguments,1),o='event handler for "'.concat(t,'"'),i=0,a=n.length;iparseInt(this.max)&&Yn(t,e[0],e,this._vnode),this.vnodeToCache=null}}},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)Yn(this.cache,t,this.keys)},mounted:function(){var t=this;this.cacheVNode(),this.$watch("include",(function(e){Xn(t,(function(t){return Jn(e,t)}))})),this.$watch("exclude",(function(e){Xn(t,(function(t){return!Jn(e,t)}))}))},updated:function(){this.cacheVNode()},render:function(){var t=this.$slots.default,e=Oe(t),n=e&&e.componentOptions;if(n){var r=Gn(n),o=this.include,i=this.exclude;if(o&&(!r||!Jn(o,r))||i&&r&&Jn(i,r))return e;var a=this.cache,s=this.keys,c=null==e.key?n.Ctor.cid+(n.tag?"::".concat(n.tag):""):e.key;a[c]?(e.componentInstance=a[c].componentInstance,_(s,c),s.push(c)):(this.vnodeToCache=e,this.keyToCache=c),e.data.keepAlive=!0}return e||t&&t[0]}}};!function(t){var e={get:function(){return B}};Object.defineProperty(t,"config",e),t.util={warn:wn,extend:A,mergeOptions:En,defineReactive:At},t.set=Tt,t.delete=Lt,t.nextTick=Ne,t.observable=function(t){return Pt(t),t},t.options=Object.create(null),F.forEach((function(e){t.options[e+"s"]=Object.create(null)})),t.options._base=t,A(t.options.components,Qn),function(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=P(arguments,1);return n.unshift(this),u(t.install)?t.install.apply(t,n):u(t)&&t.apply(null,n),e.push(t),this}}(t),function(t){t.mixin=function(t){return this.options=En(this.options,t),this}}(t),Kn(t),function(t){F.forEach((function(e){t[e]=function(t,n){return n?("component"===e&&p(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&u(n)&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}}))}(t)}(Wn),Object.defineProperty(Wn.prototype,"$isServer",{get:it}),Object.defineProperty(Wn.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(Wn,"FunctionalRenderContext",{value:hn}),Wn.version="2.7.15";var tr=g("style,class"),er=g("input,textarea,option,select,progress"),nr=g("contenteditable,draggable,spellcheck"),rr=g("events,caret,typing,plaintext-only"),or=g("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible"),ir="http://www.w3.org/1999/xlink",ar=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},sr=function(t){return ar(t)?t.slice(6,t.length):""},cr=function(t){return null==t||!1===t};function ur(t){for(var e=t.data,n=t,r=t;a(r.componentInstance);)(r=r.componentInstance._vnode)&&r.data&&(e=fr(r.data,e));for(;a(n=n.parent);)n&&n.data&&(e=fr(e,n.data));return function(t,e){if(a(t)||a(e))return lr(t,pr(e));return""}(e.staticClass,e.class)}function fr(t,e){return{staticClass:lr(t.staticClass,e.staticClass),class:a(t.class)?[t.class,e.class]:e.class}}function lr(t,e){return t?e?t+" "+e:t:e||""}function pr(t){return Array.isArray(t)?function(t){for(var e,n="",r=0,o=t.length;r-1?Ir(t,e,n):or(e)?cr(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):nr(e)?t.setAttribute(e,function(t,e){return cr(e)||"false"===e?"false":"contenteditable"===t&&rr(e)?e:"true"}(e,n)):ar(e)?cr(n)?t.removeAttributeNS(ir,sr(e)):t.setAttributeNS(ir,e,n):Ir(t,e,n)}function Ir(t,e,n){if(cr(n))t.removeAttribute(e);else{if(X&&!Y&&"TEXTAREA"===t.tagName&&"placeholder"===e&&""!==n&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",r)};t.addEventListener("input",r),t.__ieph=!0}t.setAttribute(e,n)}}var Dr={create:Rr,update:Rr};function Nr(t,e){var n=e.elm,r=e.data,o=t.data;if(!(i(r.staticClass)&&i(r.class)&&(i(o)||i(o.staticClass)&&i(o.class)))){var s=ur(e),c=n._transitionClasses;a(c)&&(s=lr(s,pr(c))),s!==n._prevClass&&(n.setAttribute("class",s),n._prevClass=s)}}var Ur,Fr={create:Nr,update:Nr};function zr(t,e,n){var r=Ur;return function o(){var i=e.apply(null,arguments);null!==i&&qr(t,o,n,r)}}var Br=Pe&&!(et&&Number(et[1])<=53);function Vr(t,e,n,r){if(Br){var o=sn,i=e;e=i._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=o||t.timeStamp<=0||t.target.ownerDocument!==document)return i.apply(this,arguments)}}Ur.addEventListener(t,e,rt?{capture:n,passive:r}:n)}function qr(t,e,n,r){(r||Ur).removeEventListener(t,e._wrapper||e,n)}function Hr(t,e){if(!i(t.data.on)||!i(e.data.on)){var n=e.data.on||{},r=t.data.on||{};Ur=e.elm||t.elm,function(t){if(a(t.__r)){var e=X?"change":"input";t[e]=[].concat(t.__r,t[e]||[]),delete t.__r}a(t.__c)&&(t.change=[].concat(t.__c,t.change||[]),delete t.__c)}(n),Ht(n,r,Vr,qr,zr,e.context),Ur=void 0}}var Wr,Kr={create:Hr,update:Hr,destroy:function(t){return Hr(t,Or)}};function Gr(t,e){if(!i(t.data.domProps)||!i(e.data.domProps)){var n,r,o=e.elm,c=t.data.domProps||{},u=e.data.domProps||{};for(n in(a(u.__ob__)||s(u._v_attr_proxy))&&(u=e.data.domProps=A({},u)),c)n in u||(o[n]="");for(n in u){if(r=u[n],"textContent"===n||"innerHTML"===n){if(e.children&&(e.children.length=0),r===c[n])continue;1===o.childNodes.length&&o.removeChild(o.childNodes[0])}if("value"===n&&"PROGRESS"!==o.tagName){o._value=r;var f=i(r)?"":String(r);Jr(o,f)&&(o.value=f)}else if("innerHTML"===n&&vr(o.tagName)&&i(o.innerHTML)){(Wr=Wr||document.createElement("div")).innerHTML="".concat(r,"");for(var l=Wr.firstChild;o.firstChild;)o.removeChild(o.firstChild);for(;l.firstChild;)o.appendChild(l.firstChild)}else if(r!==c[n])try{o[n]=r}catch(t){}}}}function Jr(t,e){return!t.composing&&("OPTION"===t.tagName||function(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}(t,e)||function(t,e){var n=t.value,r=t._vModifiers;if(a(r)){if(r.number)return y(n)!==y(e);if(r.trim)return n.trim()!==e.trim()}return n!==e}(t,e))}var Xr={create:Gr,update:Gr},Yr=O((function(t){var e={},n=/:(.+)/;return t.split(/;(?![^(]*\))/g).forEach((function(t){if(t){var r=t.split(n);r.length>1&&(e[r[0].trim()]=r[1].trim())}})),e}));function Zr(t){var e=Qr(t.style);return t.staticStyle?A(t.staticStyle,e):e}function Qr(t){return Array.isArray(t)?T(t):"string"==typeof t?Yr(t):t}var to,eo=/^--/,no=/\s*!important$/,ro=function(t,e,n){if(eo.test(e))t.style.setProperty(e,n);else if(no.test(n))t.style.setProperty(k(e),n.replace(no,""),"important");else{var r=io(e);if(Array.isArray(n))for(var o=0,i=n.length;o-1?e.split(co).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var n=" ".concat(t.getAttribute("class")||""," ");n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function fo(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(co).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{for(var n=" ".concat(t.getAttribute("class")||""," "),r=" "+e+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?t.setAttribute("class",n):t.removeAttribute("class")}}function lo(t){if(t){if("object"==typeof t){var e={};return!1!==t.css&&A(e,po(t.name||"v")),A(e,t),e}return"string"==typeof t?po(t):void 0}}var po=O((function(t){return{enterClass:"".concat(t,"-enter"),enterToClass:"".concat(t,"-enter-to"),enterActiveClass:"".concat(t,"-enter-active"),leaveClass:"".concat(t,"-leave"),leaveToClass:"".concat(t,"-leave-to"),leaveActiveClass:"".concat(t,"-leave-active")}})),ho=G&&!Y,vo="transition",mo="transitionend",yo="animation",go="animationend";ho&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(vo="WebkitTransition",mo="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(yo="WebkitAnimation",go="webkitAnimationEnd"));var bo=G?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function _o(t){bo((function(){bo(t)}))}function xo(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),uo(t,e))}function wo(t,e){t._transitionClasses&&_(t._transitionClasses,e),fo(t,e)}function Oo(t,e,n){var r=Co(t,e),o=r.type,i=r.timeout,a=r.propCount;if(!o)return n();var s="transition"===o?mo:go,c=0,u=function(){t.removeEventListener(s,f),n()},f=function(e){e.target===t&&++c>=a&&u()};setTimeout((function(){c0&&(n="transition",f=a,l=i.length):"animation"===e?u>0&&(n="animation",f=u,l=c.length):l=(n=(f=Math.max(a,u))>0?a>u?"transition":"animation":null)?"transition"===n?i.length:c.length:0,{type:n,timeout:f,propCount:l,hasTransform:"transition"===n&&$o.test(r[vo+"Property"])}}function So(t,e){for(;t.length1}function To(t,e){!0!==e.data.show&&ko(e)}var Lo=function(t){var e,n,r={},u=t.modules,f=t.nodeOps;for(e=0;e<$r.length;++e)for(r[$r[e]]=[],n=0;nh?_(t,i(n[y+1])?null:n[y+1].elm,n,d,y,r):d>y&&w(e,l,h)}(l,v,y,n,u):a(y)?(a(t.text)&&f.setTextContent(l,""),_(l,null,y,0,y.length-1,n)):a(v)?w(v,0,v.length-1):a(t.text)&&f.setTextContent(l,""):t.text!==e.text&&f.setTextContent(l,e.text),a(h)&&a(d=h.hook)&&a(d=d.postpatch)&&d(t,e)}}}function S(t,e,n){if(s(n)&&a(t.parent))t.parent.data.pendingInsert=e;else for(var r=0;r-1,a.selected!==i&&(a.selected=i);else if(I(No(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));o||(t.selectedIndex=-1)}}function Do(t,e){return e.every((function(e){return!I(e,t)}))}function No(t){return"_value"in t?t._value:t.value}function Uo(t){t.target.composing=!0}function Fo(t){t.target.composing&&(t.target.composing=!1,zo(t.target,"input"))}function zo(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function Bo(t){return!t.componentInstance||t.data&&t.data.transition?t:Bo(t.componentInstance._vnode)}var Vo={model:Ro,show:{bind:function(t,e,n){var r=e.value,o=(n=Bo(n)).data&&n.data.transition,i=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;r&&o?(n.data.show=!0,ko(n,(function(){t.style.display=i}))):t.style.display=r?i:"none"},update:function(t,e,n){var r=e.value;!r!=!e.oldValue&&((n=Bo(n)).data&&n.data.transition?(n.data.show=!0,r?ko(n,(function(){t.style.display=t.__vOriginalDisplay})):Eo(n,(function(){t.style.display="none"}))):t.style.display=r?t.__vOriginalDisplay:"none")},unbind:function(t,e,n,r,o){o||(t.style.display=t.__vOriginalDisplay)}}},qo={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function Ho(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?Ho(Oe(e.children)):t}function Wo(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var o=n._parentListeners;for(var r in o)e[C(r)]=o[r];return e}function Ko(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}var Go=function(t){return t.tag||de(t)},Jo=function(t){return"show"===t.name},Xo={name:"transition",props:qo,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(Go)).length){0;var r=this.mode;0;var o=n[0];if(function(t){for(;t=t.parent;)if(t.data.transition)return!0}(this.$vnode))return o;var i=Ho(o);if(!i)return o;if(this._leaving)return Ko(t,o);var a="__transition-".concat(this._uid,"-");i.key=null==i.key?i.isComment?a+"comment":a+i.tag:c(i.key)?0===String(i.key).indexOf(a)?i.key:a+i.key:i.key;var s=(i.data||(i.data={})).transition=Wo(this),u=this._vnode,f=Ho(u);if(i.data.directives&&i.data.directives.some(Jo)&&(i.data.show=!0),f&&f.data&&!function(t,e){return e.key===t.key&&e.tag===t.tag}(i,f)&&!de(f)&&(!f.componentInstance||!f.componentInstance._vnode.isComment)){var l=f.data.transition=A({},s);if("out-in"===r)return this._leaving=!0,Wt(l,"afterLeave",(function(){e._leaving=!1,e.$forceUpdate()})),Ko(t,o);if("in-out"===r){if(de(i))return u;var p,d=function(){p()};Wt(s,"afterEnter",d),Wt(s,"enterCancelled",d),Wt(l,"delayLeave",(function(t){p=t}))}}return o}}},Yo=A({tag:String,moveClass:String},qo);function Zo(t){t.elm._moveCb&&t.elm._moveCb(),t.elm._enterCb&&t.elm._enterCb()}function Qo(t){t.data.newPos=t.elm.getBoundingClientRect()}function ti(t){var e=t.data.pos,n=t.data.newPos,r=e.left-n.left,o=e.top-n.top;if(r||o){t.data.moved=!0;var i=t.elm.style;i.transform=i.WebkitTransform="translate(".concat(r,"px,").concat(o,"px)"),i.transitionDuration="0s"}}delete Yo.mode;var ei={Transition:Xo,TransitionGroup:{props:Yo,beforeMount:function(){var t=this,e=this._update;this._update=function(n,r){var o=Xe(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,o(),e.call(t,n,r)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,o=this.$slots.default||[],i=this.children=[],a=Wo(this),s=0;s-1?yr[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:yr[t]=/HTMLUnknownElement/.test(e.toString())},A(Wn.options.directives,Vo),A(Wn.options.components,ei),Wn.prototype.__patch__=G?Lo:L,Wn.prototype.$mount=function(t,e){return function(t,e,n){var r;t.$el=e,t.$options.render||(t.$options.render=dt),Qe(t,"beforeMount"),r=function(){t._update(t._render(),n)},new qe(t,r,L,{before:function(){t._isMounted&&!t._isDestroyed&&Qe(t,"beforeUpdate")}},!0),n=!1;var o=t._preWatchers;if(o)for(var i=0;i=0&&(e=t.slice(r),t=t.slice(0,r));var o=t.indexOf("?");return o>=0&&(n=t.slice(o+1),t=t.slice(0,o)),{path:t,query:n,hash:e}}(o.path||""),u=e&&e.path||"/",f=c.path?wi(c.path,u,n||o.append):u,l=function(t,e,n){void 0===e&&(e={});var r,o=n||ui;try{r=o(t||"")}catch(t){r={}}for(var i in e){var a=e[i];r[i]=Array.isArray(a)?a.map(ci):ci(a)}return r}(c.query,o.query,r&&r.options.parseQuery),p=o.hash||c.hash;return p&&"#"!==p.charAt(0)&&(p="#"+p),{_normalized:!0,path:f,query:l,hash:p}}var Vi,qi=function(){},Hi={name:"RouterLink",props:{to:{type:[String,Object],required:!0},tag:{type:String,default:"a"},custom:Boolean,exact:Boolean,exactPath:Boolean,append:Boolean,replace:Boolean,activeClass:String,exactActiveClass:String,ariaCurrentValue:{type:String,default:"page"},event:{type:[String,Array],default:"click"}},render:function(t){var e=this,n=this.$router,r=this.$route,o=n.resolve(this.to,r,this.append),i=o.location,a=o.route,s=o.href,c={},u=n.options.linkActiveClass,f=n.options.linkExactActiveClass,l=null==u?"router-link-active":u,p=null==f?"router-link-exact-active":f,d=null==this.activeClass?l:this.activeClass,h=null==this.exactActiveClass?p:this.exactActiveClass,v=a.redirectedFrom?pi(null,Bi(a.redirectedFrom),null,n):a;c[h]=yi(r,v,this.exactPath),c[d]=this.exact||this.exactPath?c[h]:function(t,e){return 0===t.path.replace(li,"/").indexOf(e.path.replace(li,"/"))&&(!e.hash||t.hash===e.hash)&&function(t,e){for(var n in e)if(!(n in t))return!1;return!0}(t.query,e.query)}(r,v);var m=c[h]?this.ariaCurrentValue:null,y=function(t){Wi(t)&&(e.replace?n.replace(i,qi):n.push(i,qi))},g={click:Wi};Array.isArray(this.event)?this.event.forEach((function(t){g[t]=y})):g[this.event]=y;var b={class:c},_=!this.$scopedSlots.$hasNormal&&this.$scopedSlots.default&&this.$scopedSlots.default({href:s,route:a,navigate:y,isActive:c[d],isExactActive:c[h]});if(_){if(1===_.length)return _[0];if(_.length>1||!_.length)return 0===_.length?t():t("span",{},_)}if("a"===this.tag)b.on=g,b.attrs={href:s,"aria-current":m};else{var x=function t(e){var n;if(e)for(var r=0;r-1&&(s.params[p]=n.params[p]);return s.path=zi(f.path,s.params),c(f,s,a)}if(s.path){s.params={};for(var d=0;d-1}function Oa(t,e){return wa(t)&&t._isRouter&&(null==e||t.type===e)}function $a(t,e,n){var r=function(o){o>=t.length?n():t[o]?e(t[o],(function(){r(o+1)})):r(o+1)};r(0)}function Ca(t){return function(e,n,r){var o=!1,i=0,a=null;Sa(t,(function(t,e,n,s){if("function"==typeof t&&void 0===t.cid){o=!0,i++;var c,u=Ea((function(e){var o;((o=e).__esModule||ka&&"Module"===o[Symbol.toStringTag])&&(e=e.default),t.resolved="function"==typeof e?e:Vi.extend(e),n.components[s]=e,--i<=0&&r()})),f=Ea((function(t){var e="Failed to resolve async component "+s+": "+t;a||(a=wa(t)?t:new Error(e),r(a))}));try{c=t(u,f)}catch(t){f(t)}if(c)if("function"==typeof c.then)c.then(u,f);else{var l=c.component;l&&"function"==typeof l.then&&l.then(u,f)}}})),o||r()}}function Sa(t,e){return ja(t.map((function(t){return Object.keys(t.components).map((function(n){return e(t.components[n],t.instances[n],t,n)}))})))}function ja(t){return Array.prototype.concat.apply([],t)}var ka="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;function Ea(t){var e=!1;return function(){for(var n=[],r=arguments.length;r--;)n[r]=arguments[r];if(!e)return e=!0,t.apply(this,n)}}var Pa=function(t,e){this.router=t,this.base=function(t){if(!t)if(Ki){var e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^https?:\/\/[^\/]+/,"")}else t="/";"/"!==t.charAt(0)&&(t="/"+t);return t.replace(/\/$/,"")}(e),this.current=hi,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[],this.listeners=[]};function Aa(t,e,n,r){var o=Sa(t,(function(t,r,o,i){var a=function(t,e){"function"!=typeof t&&(t=Vi.extend(t));return t.options[e]}(t,e);if(a)return Array.isArray(a)?a.map((function(t){return n(t,r,o,i)})):n(a,r,o,i)}));return ja(r?o.reverse():o)}function Ta(t,e){if(e)return function(){return t.apply(e,arguments)}}Pa.prototype.listen=function(t){this.cb=t},Pa.prototype.onReady=function(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))},Pa.prototype.onError=function(t){this.errorCbs.push(t)},Pa.prototype.transitionTo=function(t,e,n){var r,o=this;try{r=this.router.match(t,this.current)}catch(t){throw this.errorCbs.forEach((function(e){e(t)})),t}var i=this.current;this.confirmTransition(r,(function(){o.updateRoute(r),e&&e(r),o.ensureURL(),o.router.afterHooks.forEach((function(t){t&&t(r,i)})),o.ready||(o.ready=!0,o.readyCbs.forEach((function(t){t(r)})))}),(function(t){n&&n(t),t&&!o.ready&&(Oa(t,ya.redirected)&&i===hi||(o.ready=!0,o.readyErrorCbs.forEach((function(e){e(t)}))))}))},Pa.prototype.confirmTransition=function(t,e,n){var r=this,o=this.current;this.pending=t;var i,a,s=function(t){!Oa(t)&&wa(t)&&(r.errorCbs.length?r.errorCbs.forEach((function(e){e(t)})):console.error(t)),n&&n(t)},c=t.matched.length-1,u=o.matched.length-1;if(yi(t,o)&&c===u&&t.matched[c]===o.matched[u])return this.ensureURL(),t.hash&&ia(this.router,o,t,!1),s(((a=_a(i=o,t,ya.duplicated,'Avoided redundant navigation to current location: "'+i.fullPath+'".')).name="NavigationDuplicated",a));var f=function(t,e){var n,r=Math.max(t.length,e.length);for(n=0;n0)){var e=this.router,n=e.options.scrollBehavior,r=ha&&n;r&&this.listeners.push(oa());var o=function(){var n=t.current,o=Ra(t.base);t.current===hi&&o===t._startLocation||t.transitionTo(o,(function(t){r&&ia(e,t,n,!0)}))};window.addEventListener("popstate",o),this.listeners.push((function(){window.removeEventListener("popstate",o)}))}},e.prototype.go=function(t){window.history.go(t)},e.prototype.push=function(t,e,n){var r=this,o=this.current;this.transitionTo(t,(function(t){va(Oi(r.base+t.fullPath)),ia(r.router,t,o,!1),e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this,o=this.current;this.transitionTo(t,(function(t){ma(Oi(r.base+t.fullPath)),ia(r.router,t,o,!1),e&&e(t)}),n)},e.prototype.ensureURL=function(t){if(Ra(this.base)!==this.current.fullPath){var e=Oi(this.base+this.current.fullPath);t?va(e):ma(e)}},e.prototype.getCurrentLocation=function(){return Ra(this.base)},e}(Pa);function Ra(t){var e=window.location.pathname,n=e.toLowerCase(),r=t.toLowerCase();return!t||n!==r&&0!==n.indexOf(Oi(r+"/"))||(e=e.slice(t.length)),(e||"/")+window.location.search+window.location.hash}var Ma=function(t){function e(e,n,r){t.call(this,e,n),r&&function(t){var e=Ra(t);if(!/^\/#/.test(e))return window.location.replace(Oi(t+"/#"+e)),!0}(this.base)||Ia()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this;if(!(this.listeners.length>0)){var e=this.router.options.scrollBehavior,n=ha&&e;n&&this.listeners.push(oa());var r=function(){var e=t.current;Ia()&&t.transitionTo(Da(),(function(r){n&&ia(t.router,r,e,!0),ha||Fa(r.fullPath)}))},o=ha?"popstate":"hashchange";window.addEventListener(o,r),this.listeners.push((function(){window.removeEventListener(o,r)}))}},e.prototype.push=function(t,e,n){var r=this,o=this.current;this.transitionTo(t,(function(t){Ua(t.fullPath),ia(r.router,t,o,!1),e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this,o=this.current;this.transitionTo(t,(function(t){Fa(t.fullPath),ia(r.router,t,o,!1),e&&e(t)}),n)},e.prototype.go=function(t){window.history.go(t)},e.prototype.ensureURL=function(t){var e=this.current.fullPath;Da()!==e&&(t?Ua(e):Fa(e))},e.prototype.getCurrentLocation=function(){return Da()},e}(Pa);function Ia(){var t=Da();return"/"===t.charAt(0)||(Fa("/"+t),!1)}function Da(){var t=window.location.href,e=t.indexOf("#");return e<0?"":t=t.slice(e+1)}function Na(t){var e=window.location.href,n=e.indexOf("#");return(n>=0?e.slice(0,n):e)+"#"+t}function Ua(t){ha?va(Na(t)):window.location.hash=t}function Fa(t){ha?ma(Na(t)):window.location.replace(Na(t))}var za=function(t){function e(e,n){t.call(this,e,n),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,n){var r=this;this.transitionTo(t,(function(t){r.stack=r.stack.slice(0,r.index+1).concat(t),r.index++,e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this;this.transitionTo(t,(function(t){r.stack=r.stack.slice(0,r.index).concat(t),e&&e(t)}),n)},e.prototype.go=function(t){var e=this,n=this.index+t;if(!(n<0||n>=this.stack.length)){var r=this.stack[n];this.confirmTransition(r,(function(){var t=e.current;e.index=n,e.updateRoute(r),e.router.afterHooks.forEach((function(e){e&&e(r,t)}))}),(function(t){Oa(t,ya.duplicated)&&(e.index=n)}))}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:"/"},e.prototype.ensureURL=function(){},e}(Pa),Ba=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=Xi(t.routes||[],this);var e=t.mode||"hash";switch(this.fallback="history"===e&&!ha&&!1!==t.fallback,this.fallback&&(e="hash"),Ki||(e="abstract"),this.mode=e,e){case"history":this.history=new La(this,t.base);break;case"hash":this.history=new Ma(this,t.base,this.fallback);break;case"abstract":this.history=new za(this,t.base);break;default:0}},Va={currentRoute:{configurable:!0}};Ba.prototype.match=function(t,e,n){return this.matcher.match(t,e,n)},Va.currentRoute.get=function(){return this.history&&this.history.current},Ba.prototype.init=function(t){var e=this;if(this.apps.push(t),t.$once("hook:destroyed",(function(){var n=e.apps.indexOf(t);n>-1&&e.apps.splice(n,1),e.app===t&&(e.app=e.apps[0]||null),e.app||e.history.teardown()})),!this.app){this.app=t;var n=this.history;if(n instanceof La||n instanceof Ma){var r=function(t){n.setupListeners(),function(t){var r=n.current,o=e.options.scrollBehavior;ha&&o&&"fullPath"in t&&ia(e,t,r,!1)}(t)};n.transitionTo(n.getCurrentLocation(),r,r)}n.listen((function(t){e.apps.forEach((function(e){e._route=t}))}))}},Ba.prototype.beforeEach=function(t){return Ha(this.beforeHooks,t)},Ba.prototype.beforeResolve=function(t){return Ha(this.resolveHooks,t)},Ba.prototype.afterEach=function(t){return Ha(this.afterHooks,t)},Ba.prototype.onReady=function(t,e){this.history.onReady(t,e)},Ba.prototype.onError=function(t){this.history.onError(t)},Ba.prototype.push=function(t,e,n){var r=this;if(!e&&!n&&"undefined"!=typeof Promise)return new Promise((function(e,n){r.history.push(t,e,n)}));this.history.push(t,e,n)},Ba.prototype.replace=function(t,e,n){var r=this;if(!e&&!n&&"undefined"!=typeof Promise)return new Promise((function(e,n){r.history.replace(t,e,n)}));this.history.replace(t,e,n)},Ba.prototype.go=function(t){this.history.go(t)},Ba.prototype.back=function(){this.go(-1)},Ba.prototype.forward=function(){this.go(1)},Ba.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map((function(t){return Object.keys(t.components).map((function(e){return t.components[e]}))}))):[]},Ba.prototype.resolve=function(t,e,n){var r=Bi(t,e=e||this.history.current,n,this),o=this.match(r,e),i=o.redirectedFrom||o.fullPath;return{location:r,route:o,href:function(t,e,n){var r="hash"===n?"#"+e:e;return t?Oi(t+"/"+r):r}(this.history.base,i,this.mode),normalizedTo:r,resolved:o}},Ba.prototype.getRoutes=function(){return this.matcher.getRoutes()},Ba.prototype.addRoute=function(t,e){this.matcher.addRoute(t,e),this.history.current!==hi&&this.history.transitionTo(this.history.getCurrentLocation())},Ba.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==hi&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(Ba.prototype,Va);var qa=Ba;function Ha(t,e){return t.push(e),function(){var n=t.indexOf(e);n>-1&&t.splice(n,1)}}Ba.install=function t(e){if(!t.installed||Vi!==e){t.installed=!0,Vi=e;var n=function(t){return void 0!==t},r=function(t,e){var r=t.$options._parentVnode;n(r)&&n(r=r.data)&&n(r=r.registerRouteInstance)&&r(t,e)};e.mixin({beforeCreate:function(){n(this.$options.router)?(this._routerRoot=this,this._router=this.$options.router,this._router.init(this),e.util.defineReactive(this,"_route",this._router.history.current)):this._routerRoot=this.$parent&&this.$parent._routerRoot||this,r(this,this)},destroyed:function(){r(this)}}),Object.defineProperty(e.prototype,"$router",{get:function(){return this._routerRoot._router}}),Object.defineProperty(e.prototype,"$route",{get:function(){return this._routerRoot._route}}),e.component("RouterView",_i),e.component("RouterLink",Hi);var o=e.config.optionMergeStrategies;o.beforeRouteEnter=o.beforeRouteLeave=o.beforeRouteUpdate=o.created}},Ba.version="3.6.5",Ba.isNavigationFailure=Oa,Ba.NavigationFailureType=ya,Ba.START_LOCATION=hi,Ki&&window.Vue&&window.Vue.use(Ba);n(97);n(90),n(127);var Wa={"components/AlgoliaSearchBox":()=>Promise.all([n.e(0),n.e(13)]).then(n.bind(null,297)),"components/DropdownLink":()=>Promise.all([n.e(0),n.e(14)]).then(n.bind(null,254)),"components/DropdownTransition":()=>Promise.all([n.e(0),n.e(19)]).then(n.bind(null,242)),"components/Home":()=>Promise.all([n.e(0),n.e(16)]).then(n.bind(null,280)),"components/NavLink":()=>n.e(21).then(n.bind(null,241)),"components/NavLinks":()=>Promise.all([n.e(0),n.e(12)]).then(n.bind(null,265)),"components/Navbar":()=>Promise.all([n.e(0),n.e(1)]).then(n.bind(null,294)),"components/Page":()=>Promise.all([n.e(0),n.e(11)]).then(n.bind(null,281)),"components/PageEdit":()=>Promise.all([n.e(0),n.e(17)]).then(n.bind(null,267)),"components/PageNav":()=>Promise.all([n.e(0),n.e(15)]).then(n.bind(null,268)),"components/Sidebar":()=>Promise.all([n.e(0),n.e(10)]).then(n.bind(null,282)),"components/SidebarButton":()=>Promise.all([n.e(0),n.e(20)]).then(n.bind(null,283)),"components/SidebarGroup":()=>Promise.all([n.e(0),n.e(3)]).then(n.bind(null,266)),"components/SidebarLink":()=>Promise.all([n.e(0),n.e(18)]).then(n.bind(null,255)),"components/SidebarLinks":()=>Promise.all([n.e(0),n.e(3)]).then(n.bind(null,253)),"global-components/Badge":()=>Promise.all([n.e(0),n.e(4)]).then(n.bind(null,302)),"global-components/CodeBlock":()=>Promise.all([n.e(0),n.e(5)]).then(n.bind(null,298)),"global-components/CodeGroup":()=>Promise.all([n.e(0),n.e(6)]).then(n.bind(null,299)),"layouts/404":()=>n.e(7).then(n.bind(null,300)),"layouts/Layout":()=>Promise.all([n.e(0),n.e(1),n.e(2)]).then(n.bind(null,301)),NotFound:()=>n.e(7).then(n.bind(null,300)),Layout:()=>Promise.all([n.e(0),n.e(1),n.e(2)]).then(n.bind(null,301))},Ka={"v-e45badf2":()=>n.e(22).then(n.bind(null,303)),"v-01520dea":()=>n.e(23).then(n.bind(null,304)),"v-34f4f857":()=>n.e(24).then(n.bind(null,305)),"v-fb3968d8":()=>n.e(25).then(n.bind(null,306)),"v-9b173a3c":()=>n.e(27).then(n.bind(null,307)),"v-10c95498":()=>n.e(26).then(n.bind(null,308))};function Ga(t){const e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}const Ja=/-(\w)/g,Xa=Ga(t=>t.replace(Ja,(t,e)=>e?e.toUpperCase():"")),Ya=/\B([A-Z])/g,Za=Ga(t=>t.replace(Ya,"-$1").toLowerCase()),Qa=Ga(t=>t.charAt(0).toUpperCase()+t.slice(1));function ts(t,e){if(!e)return;if(t(e))return t(e);return e.includes("-")?t(Qa(Xa(e))):t(Qa(e))||t(Za(e))}const es=Object.assign({},Wa,Ka),ns=t=>es[t],rs=t=>Ka[t],os=t=>Wa[t],is=t=>Wn.component(t);function as(t){return ts(rs,t)}function ss(t){return ts(os,t)}function cs(t){return ts(ns,t)}function us(t){return ts(is,t)}function fs(...t){return Promise.all(t.filter(t=>t).map(async t=>{if(!us(t)&&cs(t)){const e=await cs(t)();Wn.component(t,e.default)}}))}function ls(t,e){"undefined"!=typeof window&&window.__VUEPRESS__&&(window.__VUEPRESS__[t]=e)}var ps=n(87),ds=n.n(ps),hs=n(88),vs=n.n(hs),ms={created(){if(this.siteMeta=this.$site.headTags.filter(([t])=>"meta"===t).map(([t,e])=>e),this.$ssrContext){const e=this.getMergedMetaTags();this.$ssrContext.title=this.$title,this.$ssrContext.lang=this.$lang,this.$ssrContext.pageMeta=(t=e)?t.map(t=>{let e="{e+=` ${n}="${vs()(t[n])}"`}),e+">"}).join("\n "):"",this.$ssrContext.canonicalLink=gs(this.$canonicalUrl)}var t},mounted(){this.currentMetaTags=[...document.querySelectorAll("meta")],this.updateMeta(),this.updateCanonicalLink()},methods:{updateMeta(){document.title=this.$title,document.documentElement.lang=this.$lang;const t=this.getMergedMetaTags();this.currentMetaTags=bs(t,this.currentMetaTags)},getMergedMetaTags(){const t=this.$page.frontmatter.meta||[];return ds()([{name:"description",content:this.$description}],t,this.siteMeta,_s)},updateCanonicalLink(){ys(),this.$canonicalUrl&&document.head.insertAdjacentHTML("beforeend",gs(this.$canonicalUrl))}},watch:{$page(){this.updateMeta(),this.updateCanonicalLink()}},beforeDestroy(){bs(null,this.currentMetaTags),ys()}};function ys(){const t=document.querySelector("link[rel='canonical']");t&&t.remove()}function gs(t=""){return t?``:""}function bs(t,e){if(e&&[...e].filter(t=>t.parentNode===document.head).forEach(t=>document.head.removeChild(t)),t)return t.map(t=>{const e=document.createElement("meta");return Object.keys(t).forEach(n=>{e.setAttribute(n,t[n])}),document.head.appendChild(e),e})}function _s(t){for(const e of["name","property","itemprop"])if(t.hasOwnProperty(e))return t[e]+e;return JSON.stringify(t)}var xs=n(89),ws={mounted(){window.addEventListener("scroll",this.onScroll)},methods:{onScroll:n.n(xs)()((function(){this.setActiveHash()}),300),setActiveHash(){const t=[].slice.call(document.querySelectorAll(".sidebar-link")),e=[].slice.call(document.querySelectorAll(".header-anchor")).filter(e=>t.some(t=>t.hash===e.hash)),n=Math.max(window.pageYOffset,document.documentElement.scrollTop,document.body.scrollTop),r=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),o=window.innerHeight+n;for(let t=0;t=i.parentElement.offsetTop+10&&(!a||n{this.$nextTick(()=>{this.$vuepress.$set("disableScrollBehavior",!1)})})}}}},beforeDestroy(){window.removeEventListener("scroll",this.onScroll)}},Os=n(22),$s=n.n(Os),Cs=[ms,ws,{mounted(){$s.a.configure({showSpinner:!1}),this.$router.beforeEach((t,e,n)=>{t.path===e.path||Wn.component(t.name)||$s.a.start(),n()}),this.$router.afterEach(()=>{$s.a.done(),this.isSidebarOpen=!1})}}],Ss={name:"GlobalLayout",computed:{layout(){const t=this.getLayout();return ls("layout",t),Wn.component(t)}},methods:{getLayout(){if(this.$page.path){const t=this.$page.frontmatter.layout;return t&&(this.$vuepress.getLayoutAsyncComponent(t)||this.$vuepress.getVueComponent(t))?t:"Layout"}return"NotFound"}}},js=n(14),ks=Object(js.a)(Ss,(function(){return(0,this._self._c)(this.layout,{tag:"component"})}),[],!1,null,null,null).exports;!function(t,e,n){switch(e){case"components":t[e]||(t[e]={}),Object.assign(t[e],n);break;case"mixins":t[e]||(t[e]=[]),t[e].push(...n);break;default:throw new Error("Unknown option name.")}}(ks,"mixins",Cs);const Es=[{name:"v-e45badf2",path:"/",component:ks,beforeEnter:(t,e,n)=>{fs("Layout","v-e45badf2").then(n)}},{path:"/index.html",redirect:"/"},{name:"v-01520dea",path:"/about/",component:ks,beforeEnter:(t,e,n)=>{fs("Layout","v-01520dea").then(n)}},{path:"/about/index.html",redirect:"/about/"},{name:"v-34f4f857",path:"/docs/",component:ks,beforeEnter:(t,e,n)=>{fs("Layout","v-34f4f857").then(n)}},{path:"/docs/index.html",redirect:"/docs/"},{name:"v-fb3968d8",path:"/docs/architecture.html",component:ks,beforeEnter:(t,e,n)=>{fs("Layout","v-fb3968d8").then(n)}},{name:"v-9b173a3c",path:"/docs/customization.html",component:ks,beforeEnter:(t,e,n)=>{fs("Layout","v-9b173a3c").then(n)}},{name:"v-10c95498",path:"/docs/config.html",component:ks,beforeEnter:(t,e,n)=>{fs("Layout","v-10c95498").then(n)}},{path:"*",component:ks}],Ps={title:"",description:"",base:"/shardcake/",headTags:[["link",{rel:"icon",href:"/shardcake/shardcake.png"}]],pages:[{title:"Home",frontmatter:{home:!0,heroImage:"/shardcake.png",actionText:"Get Started →",actionLink:"/docs/",features:[{title:"Single writer pattern",details:"Ensure that each entity exists in only one place at the time across all your servers."},{title:"Location transparency",details:"Communicate with your remote entities using their ID, regardless of their physical location."},{title:"Customization",details:"Bring your own storage, serialization, messaging protocol... or use the ones provided."}]},regularPath:"/",relativePath:"README.md",key:"v-e45badf2",path:"/"},{title:"About",frontmatter:{},regularPath:"/about/",relativePath:"about/README.md",key:"v-01520dea",path:"/about/"},{title:"Getting Started",frontmatter:{},regularPath:"/docs/",relativePath:"docs/README.md",key:"v-34f4f857",path:"/docs/",headers:[{level:2,title:"A simple use case",slug:"a-simple-use-case"},{level:2,title:"Terminology",slug:"terminology"},{level:2,title:"Key components",slug:"key-components"},{level:2,title:"An example",slug:"an-example"},{level:3,title:"Shard Manager",slug:"shard-manager"},{level:3,title:"Entity Behavior",slug:"entity-behavior"},{level:3,title:"Run the application",slug:"run-the-application"},{level:3,title:"Where to go from there?",slug:"where-to-go-from-there"}]},{title:"Architecture",frontmatter:{},regularPath:"/docs/architecture.html",relativePath:"docs/architecture.md",key:"v-fb3968d8",path:"/docs/architecture.html",headers:[{level:2,title:"Main Concepts",slug:"main-concepts"},{level:2,title:"Detailed flows",slug:"detailed-flows"},{level:3,title:"Pod Start (Sharding#register)",slug:"pod-start-sharding-register"},{level:3,title:"Pod Stop (Sharding#unregister)",slug:"pod-stop-sharding-unregister"},{level:3,title:"Sending a message to an entity (Messenger#send)",slug:"sending-a-message-to-an-entity-messenger-send"},{level:3,title:"Rebalance Algorithm",slug:"rebalance-algorithm"},{level:3,title:"Assignment Algorithm",slug:"assignment-algorithm"}]},{title:"Customization",frontmatter:{},regularPath:"/docs/customization.html",relativePath:"docs/customization.md",key:"v-9b173a3c",path:"/docs/customization.html",headers:[{level:2,title:"Storage",slug:"storage"},{level:2,title:"Messaging Protocol",slug:"messaging-protocol"},{level:2,title:"Serialization",slug:"serialization"},{level:2,title:"Health",slug:"health"}]},{title:"Configuration",frontmatter:{},regularPath:"/docs/config.html",relativePath:"docs/config.md",key:"v-10c95498",path:"/docs/config.html",headers:[{level:2,title:"Sharding Configuration",slug:"sharding-configuration"},{level:2,title:"Shard Manager Configuration",slug:"shard-manager-configuration"}]}],themeConfig:{logo:"/shardcake.png",locales:{"/":{selectText:"Language",label:"English",nav:[{text:"Documentation",link:"/docs/"},{text:"About",link:"/about/"},{text:"Github",link:"https://github.com/devsisters/shardcake"}],sidebar:{"/docs/":[{title:"Documentation",collapsable:!1,sidebarDepth:2,children:["","architecture","config","customization"]}]}}}},locales:{"/":{lang:"en-US",title:"Shardcake",description:"Entity Sharding library for Scala",path:"/"}}};n(234);Wn.component("Badge",()=>Promise.all([n.e(0),n.e(4)]).then(n.bind(null,302))),Wn.component("CodeBlock",()=>Promise.all([n.e(0),n.e(5)]).then(n.bind(null,298))),Wn.component("CodeGroup",()=>Promise.all([n.e(0),n.e(6)]).then(n.bind(null,299)));n(235);var As=[{},({Vue:t})=>{t.mixin({computed:{$dataBlock(){return this.$options.__data__block__}}})},{},{}],Ts=[];class Ls extends class{constructor(){this.store=new Wn({data:{state:{}}})}$get(t){return this.store.state[t]}$set(t,e){Wn.set(this.store.state,t,e)}$emit(...t){this.store.$emit(...t)}$on(...t){this.store.$on(...t)}}{}Object.assign(Ls.prototype,{getPageAsyncComponent:as,getLayoutAsyncComponent:ss,getAsyncComponent:cs,getVueComponent:us});var Rs={install(t){const e=new Ls;t.$vuepress=e,t.prototype.$vuepress=e}};function Ms(t,e){const n=e.toLowerCase();return t.options.routes.some(t=>t.path.toLowerCase()===n)}var Is={props:{pageKey:String,slotKey:{type:String,default:"default"}},render(t){const e=this.pageKey||this.$parent.$page.key;return ls("pageKey",e),Wn.component(e)||Wn.component(e,as(e)),Wn.component(e)?t(e):t("")}},Ds={functional:!0,props:{slotKey:String,required:!0},render:(t,{props:e,slots:n})=>t("div",{class:["content__"+e.slotKey]},n()[e.slotKey])},Ns={computed:{openInNewWindowTitle(){return this.$themeLocaleConfig.openNewWindowText||"(opens new window)"}}},Us=(n(236),n(237),Object(js.a)(Ns,(function(){var t=this._self._c;return t("span",[t("svg",{staticClass:"icon outbound",attrs:{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"}},[t("path",{attrs:{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}}),this._v(" "),t("polygon",{attrs:{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"}})]),this._v(" "),t("span",{staticClass:"sr-only"},[this._v(this._s(this.openInNewWindowTitle))])])}),[],!1,null,null,null).exports),Fs={functional:!0,render(t,{parent:e,children:n}){if(e._isMounted)return n;e.$once("hook:mounted",()=>{e.$forceUpdate()})}};Wn.config.productionTip=!1,Wn.use(qa),Wn.use(Rs),Wn.mixin(function(t,e,n=Wn){!function(t){t.locales&&Object.keys(t.locales).forEach(e=>{t.locales[e].path=e});Object.freeze(t)}(e),n.$vuepress.$set("siteData",e);const r=new(t(n.$vuepress.$get("siteData"))),o=Object.getOwnPropertyDescriptors(Object.getPrototypeOf(r)),i={};return Object.keys(o).reduce((t,e)=>(e.startsWith("$")&&(t[e]=o[e].get),t),i),{computed:i}}(t=>class{setPage(t){this.__page=t}get $site(){return t}get $themeConfig(){return this.$site.themeConfig}get $frontmatter(){return this.$page.frontmatter}get $localeConfig(){const{locales:t={}}=this.$site;let e,n;for(const r in t)"/"===r?n=t[r]:0===this.$page.path.indexOf(r)&&(e=t[r]);return e||n||{}}get $siteTitle(){return this.$localeConfig.title||this.$site.title||""}get $canonicalUrl(){const{canonicalUrl:t}=this.$page.frontmatter;return"string"==typeof t&&t}get $title(){const t=this.$page,{metaTitle:e}=this.$page.frontmatter;if("string"==typeof e)return e;const n=this.$siteTitle,r=t.frontmatter.home?null:t.frontmatter.title||t.title;return n?r?r+" | "+n:n:r||"VuePress"}get $description(){const t=function(t){if(t){const e=t.filter(t=>"description"===t.name)[0];if(e)return e.content}}(this.$page.frontmatter.meta);return t||(this.$page.frontmatter.description||this.$localeConfig.description||this.$site.description||"")}get $lang(){return this.$page.frontmatter.lang||this.$localeConfig.lang||"en-US"}get $localePath(){return this.$localeConfig.path||"/"}get $themeLocaleConfig(){return(this.$site.themeConfig.locales||{})[this.$localePath]||{}}get $page(){return this.__page?this.__page:function(t,e){for(let n=0;nn||(t.hash?!Wn.$vuepress.$get("disableScrollBehavior")&&{selector:decodeURIComponent(t.hash)}:{x:0,y:0})});!function(t){t.beforeEach((e,n,r)=>{if(Ms(t,e.path))r();else if(/(\/|\.html)$/.test(e.path))if(/\/$/.test(e.path)){const n=e.path.replace(/\/$/,"")+".html";Ms(t,n)?r(n):r()}else r();else{const n=e.path+"/",o=e.path+".html";Ms(t,o)?r(o):Ms(t,n)?r(n):r()}})}(n);const r={};try{await Promise.all(As.filter(t=>"function"==typeof t).map(e=>e({Vue:Wn,options:r,router:n,siteData:Ps,isServer:t})))}catch(t){console.error(t)}return{app:new Wn(Object.assign(r,{router:n,render:t=>t("div",{attrs:{id:"app"}},[t("RouterView",{ref:"layout"}),t("div",{class:"global-ui"},Ts.map(e=>t(e)))])})),router:n}}(!1).then(({app:t,router:e})=>{e.onReady(()=>{t.$mount("#app")})})}]); \ No newline at end of file diff --git a/assets/js/vendors~docsearch.5e19b665.js b/assets/js/vendors~docsearch.5e19b665.js new file mode 100644 index 0000000..d461918 --- /dev/null +++ b/assets/js/vendors~docsearch.5e19b665.js @@ -0,0 +1,3 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{295:function(t,e,n){ +/*! docsearch 2.6.3 | © Algolia | github.com/algolia/docsearch */ +var r;"undefined"!=typeof self&&self,r=function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:r})},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=22)}([function(t,e,n){"use strict";var r,i=n(1);function s(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}t.exports={isArray:null,isFunction:null,isObject:null,bind:null,each:null,map:null,mixin:null,isMsie:function(t){if(void 0===t&&(t=navigator.userAgent),/(msie|trident)/i.test(t)){var e=t.match(/(msie |rv:)(\d+(.\d+)?)/i);if(e)return e[2]}return!1},escapeRegExChars:function(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isNumber:function(t){return"number"==typeof t},toStr:function(t){return null==t?"":t+""},cloneDeep:function(t){var e=this.mixin({},t),n=this;return this.each(e,(function(t,r){t&&(n.isArray(t)?e[r]=[].concat(t):n.isObject(t)&&(e[r]=n.cloneDeep(t)))})),e},error:function(t){throw new Error(t)},every:function(t,e){var n=!0;return t?(this.each(t,(function(r,i){n&&(n=e.call(null,r,i,t)&&n)})),!!n):n},any:function(t,e){var n=!1;return t?(this.each(t,(function(r,i){if(e.call(null,r,i,t))return n=!0,!1})),n):n},getUniqueId:(r=0,function(){return r++}),templatify:function(t){if(this.isFunction(t))return t;var e=i.element(t);return"SCRIPT"===e.prop("tagName")?function(){return e.text()}:function(){return String(t)}},defer:function(t){setTimeout(t,0)},noop:function(){},formatPrefix:function(t,e){return e?"":t+"-"},className:function(t,e,n){return(n?"":".")+t+e},escapeHighlightedString:function(t,e,n){e=e||"";var r=document.createElement("div");r.appendChild(document.createTextNode(e)),n=n||"";var i=document.createElement("div");i.appendChild(document.createTextNode(n));var o=document.createElement("div");return o.appendChild(document.createTextNode(t)),o.innerHTML.replace(RegExp(s(r.innerHTML),"g"),e).replace(RegExp(s(i.innerHTML),"g"),n)}}},function(t,e,n){"use strict";t.exports={element:null}},function(t,e){var n=Object.prototype.hasOwnProperty,r=Object.prototype.toString;t.exports=function(t,e,i){if("[object Function]"!==r.call(e))throw new TypeError("iterator must be a function");var s=t.length;if(s===+s)for(var o=0;o was loaded but did not call our provided callback"),JSONPScriptError:s("JSONPScriptError"," + + diff --git a/docs/config.html b/docs/config.html new file mode 100644 index 0000000..5660333 --- /dev/null +++ b/docs/config.html @@ -0,0 +1,44 @@ + + + + + + Configuration | Shardcake + + + + + + + + +

# Configuration

# Sharding Configuration

Here's the list of thing you can configure on the pod side:

  • numberOfShards: number of shards

How to choose the number of shards?

numberOfShards is used to calculate the shard ID from the entity ID. For that reason, it should be the same on all pods, and can not be changed while the app is running.

If this value is lower than the number of pods, some pods will have no shards (and host no entities), which is inefficient. +If this value is too low, a difference of 1 shard between 2 pods will be quite significant and introduce some unbalance between the number of entities hosted on each pod. +On the other hand, if there are too many shards, each pod will have a lot of shards, which will produce an unnecessary overhead.

A good rule of thumb is to set the number of shards to 10x the maximum number of pods that you expect to have.

  • selfHost: hostname or IP address of the current pod
  • shardingPort: port used for pods to communicate together
  • shardManagerUri: url of the Shard Manager GraphQL API
  • serverVersion: version of the current pod

What's the version?

When performing a rolling update (upgrading all pods one by one without downtime), we want to avoid assigning shards to pods that will be stopped soon. +Ideally, we want to move each shard only once during the whole process.

This serverVersion allows the Shard Manager to know which pods are old and which are new. It will then pick the new pods to assign shards.

  • entityMaxIdleTime: time of inactivity (without receiving any message) after which an entity will be stopped

Termination Message

registerEntity takes an optional parameter called terminationMessage. This allows defining a message that will be sent to your entities before they are stopped (either by a rebalance or because of inactivity).

If no termination message is provided, the entity queues will simply be shutdown. +But if you want to ensure entities are stopped "cleanly" after processing their last message, define a termination message and stop the behavior yourself (by calling ZIO.interrupt) after receiving that message.

Termination messages must contain a promise which you need to complete to indicate that shutdown is complete. See the example here (opens new window).

  • entityTerminationTimeout: time we give to an entity to handle the termination message before interrupting it
  • sendTimeout: timeout when calling sendMessage
  • refreshAssignmentsRetryInterval: retry interval in case of failure getting shard assignments from storage
  • unhealthyPodReportInterval: interval to report unhealthy pods to the Shard Manager (this exists to prevent calling the Shard Manager for each failed message)
  • simulateRemotePods: disable optimizations when sending a message to an entity hosted on the local shards (this will force serialization of all messages)

# Shard Manager Configuration

Here's the list of thing you can configure on the Shard Manager side:

  • numberOfShards: number of shards (see above)
  • apiPort: port to expose the GraphQL API
  • rebalanceInterval: interval for regular rebalancing of shards
  • rebalanceRetryInterval: retry interval for rebalancing when some shards failed to be rebalanced
  • pingTimeout: time to wait for a pod to respond to a ping request
  • persistRetryInterval: retry interval for persistence of pods and shard assignments
  • persistRetryCount: max retry count for persistence of pods and shard assignments
  • rebalanceRate: max ratio of shards to rebalance in a single iteration

Rebalance Rate

The rebalance rate is there to prevent too many shards being assigned immediately to new pods. +Instead of reaching a perfect spread right away, we can use several iterations to make sure new pods are able to handle the new shards. +This is particularly useful if starting entities has a performance cost (e.g. loading state) and we don't want to start too many at once.

When a pod is leaving, its shards need to be immediately rebalanced so the ratio is not used in that case.

+ + + diff --git a/docs/customization.html b/docs/customization.html new file mode 100644 index 0000000..6642817 --- /dev/null +++ b/docs/customization.html @@ -0,0 +1,92 @@ + + + + + + Customization | Shardcake + + + + + + + + +

# Customization

As demonstrated in the Getting Started, there are several parts of the system that are entirely customizable. +For each of them, Shardcake provides at least a fake implementation for testing and a prod-ready implementation using common technologies.

Feel free to implement your own!

architecture diagram

# Storage

The Storage trait defines how to store and access pods and shards assignments. +It contains 5 methods: getAssignments/saveAssignments to store assignments, getPods/savePods to store pods, and assignmentsStream to receive assignment updates.

trait Storage {
+  def getAssignments: Task[Map[ShardId, Option[PodAddress]]]
+  def saveAssignments(assignments: Map[ShardId, Option[PodAddress]]): Task[Unit]
+  
+  def assignmentsStream: ZStream[Any, Throwable, Map[Int, Option[PodAddress]]]
+  
+  def getPods: Task[Map[PodAddress, Pod]]
+  def savePods(pods: Map[PodAddress, Pod]): Task[Unit]
+}
+

For testing, you can use the Storage.memory layer that keeps data in memory.

Shardcake provides an implementation of Storage using Redis. To use it, add the following dependency:

libraryDependencies += "com.devsisters" %% "shardcake-storage-redis" % "2.1.0"
+

You can then simply use the StorageRedis.live layer.

It requires a RedisConfig with the following options:

  • assignmentsKey: the key to store shard assignments in Redis
  • podsKey: the key to store registered pods in Redis

It also requires a Redis object, which is an alias to RedisCommands[Task, String, String] with PubSubCommands[fs2Stream, String, String] from the redis4cats (opens new window) library used under the hood. +Here's an example how to build it:

import com.devsisters.shardcake.StorageRedis.Redis
+import dev.profunktor.redis4cats.Redis
+import dev.profunktor.redis4cats.connection.RedisClient
+import dev.profunktor.redis4cats.data.RedisCodec
+import dev.profunktor.redis4cats.effect.Log
+import dev.profunktor.redis4cats.pubsub.PubSub
+import zio.interop.catz._
+import zio.{ Task, ZEnvironment, ZIO, ZLayer }
+
+val redis: ZLayer[Any, Throwable, Redis] =
+  ZLayer.scopedEnvironment {
+    implicit val runtime: zio.Runtime[Any] = zio.Runtime.default
+    implicit val logger: Log[Task]         = new Log[Task] {
+      override def debug(msg: => String): Task[Unit] = ZIO.unit
+      override def error(msg: => String): Task[Unit] = ZIO.logError(msg)
+      override def info(msg: => String): Task[Unit]  = ZIO.logDebug(msg)
+    }
+
+    (for {
+      client   <- RedisClient[Task].from("redis://foobared@localhost")
+      commands <- Redis[Task].fromClient(client, RedisCodec.Utf8)
+      pubSub   <- PubSub.mkPubSubConnection[Task, String, String](client, RedisCodec.Utf8)
+    } yield ZEnvironment(commands, pubSub)).toScopedZIO
+  }
+

# Messaging Protocol

The Pods trait defines how to communicate with remote pods. +It is used both by the Shard Manager for assigning and unassigning shards, and by pods for internal communication (forward messages to each other).

trait Pods {
+  def assignShards(pod: PodAddress, shards: Set[ShardId]): Task[Unit]
+  def unassignShards(pod: PodAddress, shards: Set[ShardId]): Task[Unit]
+  def ping(pod: PodAddress): Task[Unit]
+  
+  def sendMessage(pod: PodAddress, message: BinaryMessage): Task[Option[Array[Byte]]]
+  def sendMessageStreaming(pod: PodAddress, message: BinaryMessage): ZStream[Any, Throwable, Array[Byte]]
+}
+

For testing, you can use the Pods.noop layer that does nothing.

Shardcake provides an implementation of Pods using the gRPC protocol. To use it, add the following dependency:

libraryDependencies += "com.devsisters" %% "shardcake-protocol-grpc" % "2.1.0"
+

You can then simply use the GrpcPods.live layer.

On pods, you also need expose the gRPC API. This is done by adding the GrpcShardingService.live layer to your environment. You don't need this one on the Shard Manager.

# Serialization

The Serialization trait defines how to serialize user messages that will be sent between pods. +It contains 2 methods encode and decode that define how to transform a give type from and to bytes.

trait Serialization {
+  def encode(message: Any): Task[Array[Byte]]
+  def decode[A](bytes: Array[Byte]): Task[A]
+}
+

For testing, you can use the Serialization.javaSerialization layer that uses Java Serialization (not recommended in production).

Shardcake provides an implementation of Serialization using the Kryo (opens new window) binary serialization library. To use it, add the following dependency:

libraryDependencies += "com.devsisters" %% "shardcake-serialization-kryo" % "2.1.0"
+

You can then simply use the KryoSerialization.live layer.

Server updates and message versioning

  • Messages are not persisted, which means that if you stop and restart the whole system, you can change anything in the messages format.
  • On the other hand, if you wish to do rolling updates (update servers progressively without downtime), you need to be careful with changes in the messages format.
  • What you can do largely depends on your serialization mechanism, some solutions allow changes while some others are very restrictive. +Kryo (opens new window) by default is pretty strict and won't support most changes, but there are settings to support more (at the cost of some performance or message size).
  • When you can't modify existing messages, an option is to create new messages that won't be used until the rolling update is finished (so you won't have cases where old nodes receive new messages).

# Health

The PodsHealth trait defines how to know if a pod is still alive or is dead (in which case, we should reassign all its shards).

trait PodsHealth {
+  def isAlive(podAddress: PodAddress): UIO[Boolean]
+}
+

For testing, you can use the PodsHealth.noop layer that always returns true, or the PodsHealth.local layer that uses ping from the Messaging Protocol to check if a pod is alive.

Shardcake provides an implementation of PodsHealth using the Kubernetes (opens new window) API. To use it, add the following dependency:

libraryDependencies += "com.devsisters" %% "shardcake-health-k8s" % "2.1.0"
+

You can then simply use the K8sPodsHealth.live layer. This is requiring a Pods layer that comes from zio-k8s (opens new window).

Examples

Check the examples (opens new window) folder that contains a full example using Redis, gRPC and Kryo seralization.

+ + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..56e59c7 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,139 @@ + + + + + + Getting Started | Shardcake + + + + + + + + +

# Getting Started

Shardcake is a Scala open source library that makes it easy to distribute entities across multiple servers and interact with those entities using their ID without knowing their actual location (this is also known as location transparency).

Shardcake exposes a purely functional API and depends heavily on ZIO (opens new window). It is recommended to be familiar with ZIO to read this documentation.

# A simple use case

We are building a multiplayer game where users can join guilds. +We expect our game to be successful, so we need to be able to scale out (deploy it on multiple servers to handle the load).

A guild is limited to 30 members. +Let's consider the case where 2 users try to join a guild at the exact same time, but our guild already had 29 members.

naive diagram

If we implement this naively, 2 different servers might receive our 2 requests to join the guild. +They will both check the current size of the guild at the same time, which will be 29 and they will both accept the new guild member. Now our guild has 31 members 😱.

There are 2 common approaches to deal with this issue:

  • Global lock approach: when checking the members of the guild, acquire a lock that is shared between the different game servers, and release it after saving the new member. +That way, if 2 different game servers try to do it at the same time, the 2nd one will wait for the first one to finish.
  • Single writer approach: instead of handling the requests on 2 game servers concurrently, redirect the 2 requests to a single entity that will handle them sequentially.

Entity Sharding is a way to implement the second approach. In this case our entities (here, our guilds) will be spread across our game servers so that +each entity exists in only one place at the time.

single writer diagram

If our entity might be on any game server, how do we know where it is? This is the second characteristic of entity sharding, known as location transparency: we only need to know the entity ID. +Using the entity ID, the sharding system will be able to find on which server the entity is located.

Shardcake provides components to:

  • automatically manage the assignments of entities to game servers
  • send messages to your entities using their IDs

Once sharding is setup, it will let you write code like the following (sending a message to guild regardless of its location):

def joinGuild(guildId: GuildId): ZIO[Context, Throwable, GuildState] =
+  for {
+    userId     <- getCurrentUserFromContext
+    guildState <- guild.send(guildId)(JoinGuild(userId, _))
+  } yield guildState
+

# Terminology

Before we go further, let's define a few terms:

  • An entity is a small message handler that can be addressed by ID. +For example, User A or Guild B are entities. In this case we say that User and Guild are entity types.
  • A pod is an application server that can host entities. +Entities usually run on multiple pods, but a single entity will only run on a single pod at the time. You will never have the same entity running on 2 different pods.
  • A shard is a logical group of entities that will always be located on the same pod. +There might be millions of entities, so instead of keeping millions of entities-to-pods mappings, we group entities into shards and maintain reasonably-sized shards-to-pods mappings.

# Key components

Shardcake is composed of 2 main components:

  • The Shard Manager is an independent component that needs a single instance running. It is in charge of assigning shards to pods.
  • Entities will run on your application servers and process messages that are sent to them. Note that the entity behavior (including entity persistence) is entirely up to you. +Shardcake only takes care of starting entities on the right pods as well as the communication between them.

There are 4 pluggable parts that can be implemented with the technology of your choice.

  • The Storage trait defines where shard assignments will be stored. Shardcake provides an implementation using Redis.
  • The Pods trait defines how to communicate with remote pods. Shardcake provides an implementation using gRPC as the protocol.
  • The Serialization defines how to encode and decode messages. Shardcake provides an implementation using Kryo.
  • The PodsHealth trait defines how to check if a pod is healthy or not. Shardcake provides an implementation using the k8s API.

architecture diagram

# An example

# Shard Manager

The first thing we need to do is starting the Shard Manager. This component is available by importing the shardcake-manager dependency, and requires implementations of Storage, Pods and PodsHealth to work.

To make it simpler and run our example without 3rd parties, we're going to run a simple PodsHealth implementation that just pings a pod to see if it's alive, and in-memory Storage. +We need a proper messaging protocol to communicate with pods, so we're going to use shardcake-protocol-grpc.

libraryDependencies += "com.devsisters" %% "shardcake-manager"       % "2.1.0"
+libraryDependencies += "com.devsisters" %% "shardcake-protocol-grpc" % "2.1.0"
+

The Shard Manager exposes a small GraphQL API, which means we need to start a small webserver. This can be done by calling Server.run and providing all the required dependencies.

import com.devsisters.shardcake._
+import com.devsisters.shardcake.interfaces._
+import zio._
+
+object ShardManagerApp extends ZIOAppDefault {
+  def run: Task[Nothing] =
+    Server.run.provide(
+      ZLayer.succeed(ManagerConfig.default),
+      ZLayer.succeed(GrpcConfig.default),
+      PodsHealth.local, // just ping a pod to see if it's alive
+      GrpcPods.live,    // use gRPC protocol
+      Storage.memory,   // store data in memory
+      ShardManager.live // shard manager logic
+    )
+}
+

That's it! Running this small app will start the Shard Manager and its API. It is now ready to receive registration from pods.

# Entity Behavior

We now need to define our entity behavior: what kind of messages can our entity receive, and what to do when it receives those messages. We will model our Guild example.

First, we need the following dependencies:

libraryDependencies += "com.devsisters" %% "shardcake-entities"      % "2.1.0"
+libraryDependencies += "com.devsisters" %% "shardcake-protocol-grpc" % "2.1.0"
+

Let's start with defining the messages our entities can receive. We will have 2: one for joining a guild and one for leaving. +We create a sealed trait that will contain all the possible message types. +Any message that can be replied needs to contain a Replier[A] where A is the response type.

sealed trait GuildMessage
+
+object GuildMessage {
+  case class Join(userId: String, replier: Replier[Try[Set[String]]]) extends GuildMessage
+  case class Leave(userId: String)                                    extends GuildMessage
+}
+

We also need to define an Entity Type. This is done by extending EntityType with the message type as well as a unique String identifier for this type.

object Guild extends EntityType[GuildMessage]("guild")
+

The behavior itself is a function with the following signature:

def behavior(entityId: String, messages: Queue[GuildMessage]): RIO[Sharding, Nothing]
+

It takes an entityId and a Queue[GuildMessage] and returns a ZIO that never ends (hence the return type Nothing). +That function is just supposed to consume messages forever.

Let's first define how to handle a single GuildMessage.

We will use a Ref[Set[String]] to hold our state (the list of users in the guild). +When we receive a Join request, we check if the guild is already full (here, we hardcode the max to 5) and respond with a failure or modify the state and respond with a success. +We use replier.reply to send a response to the sender (here, sending the full list of guild members wrapped in a Try to express errors).

def handleMessage(state: Ref[Set[String]], message: GuildMessage): RIO[Sharding, Unit] =
+  message match {
+    case GuildMessage.Join(userId, replier) =>
+      state.get.flatMap(members =>
+        if (members.size >= 5)
+          replier.reply(Failure(new Exception("Guild is already full!")))
+        else
+          state.updateAndGet(_ + userId).flatMap { newMembers =>
+            replier.reply(Success(newMembers))
+          }
+      )
+    case GuildMessage.Leave(userId)         =>
+      state.update(_ - userId)
+  }
+

We are now ready to create our behavior, starting from an empty state when the entity is created:

def behavior(entityId: String, messages: Queue[GuildMessage]): RIO[Sharding, Nothing] =
+  Ref
+    .make(Set.empty[String])
+    .flatMap(state => messages.take.flatMap(handleMessage(state, _)).forever)
+

# Run the application

To run our entities, we need to register their behavior to the Sharding system, which is done by calling Sharding.registerEntity, passing the entity type and the behavior.

We then need to notify the Shard Manager that a new pod is now ready to run entities, and shards may be assigned to it. +This is done by calling Sharding.registerScoped (which is the equivalent of calling Sharding.register when the program starts and Sharding.unregister when the program ends).

To communicate with our entities, we need a Messenger[GuildMessage], which we get by calling Sharding.messenger. You can even use messenger on a pod that is not hosting any entities.

  val program =
+    for {
+      _     <- Sharding.registerEntity(Guild, behavior)
+      _     <- Sharding.registerScoped
+      guild <- Sharding.messenger(Guild)
+      _     <- guild.send("guild1")(Join("user1", _)).debug
+      _     <- guild.send("guild1")(Join("user2", _)).debug
+      _     <- guild.send("guild1")(Join("user3", _)).debug
+      _     <- guild.send("guild1")(Join("user4", _)).debug
+      _     <- guild.send("guild1")(Join("user5", _)).debug
+      _     <- guild.send("guild1")(Join("user6", _)).debug
+    } yield ()
+

Finally, we provide all required dependencies as we did for the Shard Manager.

def run: Task[Unit] =
+  ZIO.scoped(program).provide(
+    ZLayer.succeed(Config.default),
+    ZLayer.succeed(GrpcConfig.default),
+    Serialization.javaSerialization, // use java serialization for messages
+    Storage.memory,                  // store data in memory
+    ShardManagerClient.liveWithSttp, // client to communicate with the Shard Manager
+    GrpcPods.live,                   // use gRPC protocol
+    GrpcShardingService.live,        // expose gRPC service
+    Sharding.live                    // sharding logic
+  )
+

We can now run our program. It will successively send 6 Join messages to our guild and print the result:

Success(Set(user1))
+Success(Set(user1, user2))
+Success(Set(user1, user2, user3))
+Success(Set(user1, user2, user3, user4))
+Success(HashSet(user1, user5, user4, user2, user3))
+Failure(java.lang.Exception: Guild is already full!)
+

# One more thing...

Sharding also exposes 2 methods that can be interesting for some use cases:

  • registerSingleton lets you register some background processes that will run in only one pod at any given time. Singletons can't receive messages and they are not stopped in case of inactivity.
  • registerTopic lets you broadcast messages to all registered pods (use Sharding.broadcaster instead of Sharding.messenger).

# Where to go from there?

In this simple example, we only had a single pod so there was no real benefit from using sharding. +But we could run the exact same code on multiple pods, and the sharding system would ensure that each guild is only running on a single pod. +In other words, all messages sent to guild1 would be handled by the same pod.

The only changes we would need to make would be:

  • to use an actual Storage implementation for sharding (e.g. Redis)
  • to save our guild state somewhere instead of in memory, so that we don't lose this state if the guild entity is moved from one pod to another

The running code for this example can be found here (opens new window), +as well as a more complex example (opens new window) using Redis to persist data and where you can run multiple pods.

To understand how Sharding works under the hood, have a look at the Architecture section. +The Configuration section explains how to configure the sharding system. +Finally, the Customization section describes how you can use your own storage, serialization or messaging protocol, as well as the options provided by Shardcake.

Differences with Akka Cluster Sharding ?

Akka Cluster Sharding (opens new window) is the main alternative for sharding in Scala. +Here is how Shardcake differs from Akka:

  • Shardcake is a purely functional library based on ZIO, while Akka is a library leaning towards the "better Java" style of Scala and is based on Future.

  • With Akka, all application servers must be part of a cluster. This is particularly tricky to configure and sensitive to all kinds of failures. +This is not the case with Shardcake since the Shard Manager is an external component that relies on systems like Kubernetes to monitor pod health.

  • Akka is way more than a sharding library, this is basically an entire framework that has a lot of features. +Shardcake is much simpler and modular. It lets you customize multiple things, including the messaging protocol between pods.

  • Akka is backed by Lightbend (opens new window), who provides commercial support, +while Shardcake is backed by Devsisters (opens new window) and the ZIO community.

+ + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..0dd339d --- /dev/null +++ b/index.html @@ -0,0 +1,36 @@ + + + + + + Shardcake + + + + + + + + +
hero

+ Shardcake +

+ Entity Sharding library for Scala +

+ Get Started → +

Single writer pattern

Ensure that each entity exists in only one place at the time across all your servers.

Location transparency

Communicate with your remote entities using their ID, regardless of their physical location.

Customization

Bring your own storage, serialization, messaging protocol... or use the ones provided.

+ + + diff --git a/kingdom.png b/kingdom.png new file mode 100644 index 0000000..45d0d06 Binary files /dev/null and b/kingdom.png differ diff --git a/shardcake.png b/shardcake.png new file mode 100644 index 0000000..125e95a Binary files /dev/null and b/shardcake.png differ diff --git a/usecase1.png b/usecase1.png new file mode 100644 index 0000000..88e997d Binary files /dev/null and b/usecase1.png differ diff --git a/usecase2.png b/usecase2.png new file mode 100644 index 0000000..50f0cf0 Binary files /dev/null and b/usecase2.png differ