diff --git a/404.html b/404.html index c4e5b10..d97ce3f 100644 --- a/404.html +++ b/404.html @@ -1,19 +1,21 @@ - + - - - + +Page Not Found | foomo project docs -Page Not Found | foomo project docs - - + + + + + + - +
-
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

+ + \ No newline at end of file diff --git a/assets/css/styles.47738ace.css b/assets/css/styles.47738ace.css deleted file mode 100644 index 9abcc23..0000000 --- a/assets/css/styles.47738ace.css +++ /dev/null @@ -1 +0,0 @@ -.container,.row .col{padding:0 var(--ifm-spacing-horizontal);width:100%}.row .col,img{max-width:100%}.markdown>h2,.markdown>h3,.markdown>h4,.markdown>h5,.markdown>h6{margin-bottom:calc(var(--ifm-heading-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown li,body{word-wrap:break-word}body,ol ol,ol ul,ul ol,ul ul{margin:0}pre,table{overflow:auto}blockquote,pre{margin:0 0 var(--ifm-spacing-vertical)}.breadcrumbs__link,.button{transition-timing-function:var(--ifm-transition-timing-default)}.button,.toggle_iYfV{-webkit-user-select:none;-ms-user-select:none}.button,code{vertical-align:middle}.button--outline.button--active,.button--outline:active,.button--outline:hover,:root{--ifm-button-color:var(--ifm-font-color-base-inverse)}.avatar__photo,.card,.text--truncate{overflow:hidden}.menu__link:hover,a{transition:color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.navbar--dark,:root{--ifm-navbar-link-hover-color:var(--ifm-color-primary)}.menu,.navbar-sidebar{overflow-x:hidden}:root,html[data-theme=dark]{--ifm-color-emphasis-500:var(--ifm-color-gray-500)}.admonition-icon svg,.alert__icon svg{fill:var(--ifm-alert-foreground-color)}.toggle_iYfV,html{-webkit-tap-highlight-color:transparent}*,.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}.DocSearch-Hit-content-wrapper,.navbar__title,.text--truncate{text-overflow:ellipsis;white-space:nowrap}.button,.dropdown__link,.navbar__title,.text--truncate{white-space:nowrap}:root{--ifm-color-scheme:light;--ifm-dark-value:10%;--ifm-darker-value:15%;--ifm-darkest-value:30%;--ifm-light-value:15%;--ifm-lighter-value:30%;--ifm-lightest-value:50%;--ifm-contrast-background-value:90%;--ifm-contrast-foreground-value:70%;--ifm-contrast-background-dark-value:70%;--ifm-contrast-foreground-dark-value:90%;--ifm-color-primary:#3578e5;--ifm-color-secondary:#ebedf0;--ifm-color-success:#00a400;--ifm-color-info:#54c7ec;--ifm-color-warning:#ffba00;--ifm-color-danger:#fa383e;--ifm-color-primary-dark:#306cce;--ifm-color-primary-darker:#2d66c3;--ifm-color-primary-darkest:#2554a0;--ifm-color-primary-light:#538ce9;--ifm-color-primary-lighter:#72a1ed;--ifm-color-primary-lightest:#9abcf2;--ifm-color-primary-contrast-background:#ebf2fc;--ifm-color-primary-contrast-foreground:#102445;--ifm-color-secondary-dark:#d4d5d8;--ifm-color-secondary-darker:#c8c9cc;--ifm-color-secondary-darkest:#a4a6a8;--ifm-color-secondary-light:#eef0f2;--ifm-color-secondary-lighter:#f1f2f5;--ifm-color-secondary-lightest:#f5f6f8;--ifm-color-secondary-contrast-background:#fdfdfe;--ifm-color-secondary-contrast-foreground:#474748;--ifm-color-success-dark:#009400;--ifm-color-success-darker:#008b00;--ifm-color-success-darkest:#007300;--ifm-color-success-light:#26b226;--ifm-color-success-lighter:#4dbf4d;--ifm-color-success-lightest:#80d280;--ifm-color-success-contrast-background:#e6f6e6;--ifm-color-success-contrast-foreground:#003100;--ifm-color-info-dark:#4cb3d4;--ifm-color-info-darker:#47a9c9;--ifm-color-info-darkest:#3b8ba5;--ifm-color-info-light:#6ecfef;--ifm-color-info-lighter:#87d8f2;--ifm-color-info-lightest:#aae3f6;--ifm-color-info-contrast-background:#eef9fd;--ifm-color-info-contrast-foreground:#193c47;--ifm-color-warning-dark:#e6a700;--ifm-color-warning-darker:#d99e00;--ifm-color-warning-darkest:#b38200;--ifm-color-warning-light:#ffc426;--ifm-color-warning-lighter:#ffcf4d;--ifm-color-warning-lightest:#ffdd80;--ifm-color-warning-contrast-background:#fff8e6;--ifm-color-warning-contrast-foreground:#4d3800;--ifm-color-danger-dark:#e13238;--ifm-color-danger-darker:#d53035;--ifm-color-danger-darkest:#af272b;--ifm-color-danger-light:#fb565b;--ifm-color-danger-lighter:#fb7478;--ifm-color-danger-lightest:#fd9c9f;--ifm-color-danger-contrast-background:#ffebec;--ifm-color-danger-contrast-foreground:#4b1113;--ifm-color-white:#fff;--ifm-color-black:#000;--ifm-color-gray-0:var(--ifm-color-white);--ifm-color-gray-100:#f5f6f7;--ifm-color-gray-200:#ebedf0;--ifm-color-gray-300:#dadde1;--ifm-color-gray-400:#ccd0d5;--ifm-color-gray-500:#bec3c9;--ifm-color-gray-600:#8d949e;--ifm-color-gray-700:#606770;--ifm-color-gray-800:#444950;--ifm-color-gray-900:#1c1e21;--ifm-color-gray-1000:var(--ifm-color-black);--ifm-color-emphasis-0:var(--ifm-color-gray-0);--ifm-color-emphasis-100:var(--ifm-color-gray-100);--ifm-color-emphasis-200:var(--ifm-color-gray-200);--ifm-color-emphasis-300:var(--ifm-color-gray-300);--ifm-color-emphasis-400:var(--ifm-color-gray-400);--ifm-color-emphasis-600:var(--ifm-color-gray-600);--ifm-color-emphasis-700:var(--ifm-color-gray-700);--ifm-color-emphasis-800:var(--ifm-color-gray-800);--ifm-color-emphasis-900:var(--ifm-color-gray-900);--ifm-color-emphasis-1000:var(--ifm-color-gray-1000);--ifm-color-content:var(--ifm-color-emphasis-900);--ifm-color-content-inverse:var(--ifm-color-emphasis-0);--ifm-color-content-secondary:#525860;--ifm-background-color:transparent;--ifm-background-surface-color:var(--ifm-color-content-inverse);--ifm-global-border-width:1px;--ifm-global-radius:0.4rem;--ifm-hover-overlay:rgba(0,0,0,.05);--ifm-font-color-base:var(--ifm-color-content);--ifm-font-color-base-inverse:var(--ifm-color-content-inverse);--ifm-font-color-secondary:var(--ifm-color-content-secondary);--ifm-font-family-base:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--ifm-font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--ifm-font-size-base:100%;--ifm-font-weight-light:300;--ifm-font-weight-normal:400;--ifm-font-weight-semibold:500;--ifm-font-weight-bold:700;--ifm-font-weight-base:var(--ifm-font-weight-normal);--ifm-line-height-base:1.65;--ifm-global-spacing:1rem;--ifm-spacing-vertical:var(--ifm-global-spacing);--ifm-spacing-horizontal:var(--ifm-global-spacing);--ifm-transition-fast:200ms;--ifm-transition-slow:400ms;--ifm-transition-timing-default:cubic-bezier(0.08,0.52,0.52,1);--ifm-global-shadow-lw:0 1px 2px 0 rgba(0,0,0,.1);--ifm-global-shadow-md:0 5px 40px rgba(0,0,0,.2);--ifm-global-shadow-tl:0 12px 28px 0 rgba(0,0,0,.2),0 2px 4px 0 rgba(0,0,0,.1);--ifm-z-index-dropdown:3;--ifm-z-index-fixed:4;--ifm-z-index-overlay:6;--ifm-container-width:1140px;--ifm-container-width-xl:1320px;--ifm-code-background:#f6f7f8;--ifm-code-border-radius:var(--ifm-global-radius);--ifm-code-font-size:90%;--ifm-code-padding-horizontal:0.1rem;--ifm-code-padding-vertical:0.1rem;--ifm-pre-background:var(--ifm-color-emphasis-100);--ifm-pre-border-radius:var(--ifm-code-border-radius);--ifm-pre-color:inherit;--ifm-pre-line-height:1.45;--ifm-pre-padding:1rem;--ifm-heading-color:inherit;--ifm-heading-margin-top:0;--ifm-heading-margin-bottom:var(--ifm-spacing-vertical);--ifm-heading-font-family:var(--ifm-font-family-base);--ifm-heading-font-weight:var(--ifm-font-weight-bold);--ifm-heading-line-height:1.25;--ifm-h1-font-size:2rem;--ifm-h2-font-size:1.5rem;--ifm-h3-font-size:1.25rem;--ifm-h4-font-size:1rem;--ifm-h5-font-size:0.875rem;--ifm-h6-font-size:0.85rem;--ifm-image-alignment-padding:1.25rem;--ifm-leading-desktop:1.25;--ifm-leading:calc(var(--ifm-leading-desktop)*1rem);--ifm-list-left-padding:2rem;--ifm-list-margin:1rem;--ifm-list-item-margin:0.25rem;--ifm-list-paragraph-margin:1rem;--ifm-table-cell-padding:0.75rem;--ifm-table-background:transparent;--ifm-table-stripe-background:var(--ifm-color-emphasis-100);--ifm-table-border-width:1px;--ifm-table-border-color:var(--ifm-color-emphasis-300);--ifm-table-head-background:inherit;--ifm-table-head-color:inherit;--ifm-table-head-font-weight:var(--ifm-font-weight-bold);--ifm-table-cell-color:inherit;--ifm-link-color:var(--ifm-color-primary);--ifm-link-decoration:none;--ifm-link-hover-color:var(--ifm-link-color);--ifm-link-hover-decoration:underline;--ifm-paragraph-margin-bottom:var(--ifm-leading);--ifm-blockquote-font-size:var(--ifm-font-size-base);--ifm-blockquote-border-left-width:2px;--ifm-blockquote-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-blockquote-padding-vertical:0;--ifm-blockquote-shadow:none;--ifm-blockquote-color:var(--ifm-color-emphasis-800);--ifm-blockquote-border-color:var(--ifm-color-emphasis-300);--ifm-hr-border-color:var(--ifm-color-emphasis-500);--ifm-hr-border-width:1px;--ifm-hr-margin-vertical:1.5rem;--ifm-scrollbar-size:7px;--ifm-scrollbar-track-background-color:#f1f1f1;--ifm-scrollbar-thumb-background-color:silver;--ifm-scrollbar-thumb-hover-background-color:#a7a7a7;--ifm-alert-background-color:inherit;--ifm-alert-border-color:inherit;--ifm-alert-border-radius:var(--ifm-global-radius);--ifm-alert-border-width:0px;--ifm-alert-border-left-width:5px;--ifm-alert-color:var(--ifm-font-color-base);--ifm-alert-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-alert-padding-vertical:var(--ifm-spacing-vertical);--ifm-alert-shadow:var(--ifm-global-shadow-lw);--ifm-avatar-intro-margin:1rem;--ifm-avatar-intro-alignment:inherit;--ifm-avatar-photo-size-sm:2rem;--ifm-avatar-photo-size-md:3rem;--ifm-avatar-photo-size-lg:4rem;--ifm-avatar-photo-size-xl:6rem;--ifm-badge-background-color:inherit;--ifm-badge-border-color:inherit;--ifm-badge-border-radius:var(--ifm-global-radius);--ifm-badge-border-width:var(--ifm-global-border-width);--ifm-badge-color:var(--ifm-color-white);--ifm-badge-padding-horizontal:calc(var(--ifm-spacing-horizontal)*0.5);--ifm-badge-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-breadcrumb-border-radius:1.5rem;--ifm-breadcrumb-spacing:0.0625rem;--ifm-breadcrumb-color-active:var(--ifm-color-primary);--ifm-breadcrumb-item-background-active:var(--ifm-hover-overlay);--ifm-breadcrumb-padding-horizontal:1rem;--ifm-breadcrumb-padding-vertical:0.5rem;--ifm-breadcrumb-size-multiplier:1;--ifm-breadcrumb-separator:url('data:image/svg+xml;utf8,');--ifm-breadcrumb-separator-filter:none;--ifm-breadcrumb-separator-size:0.5rem;--ifm-breadcrumb-separator-size-multiplier:1.25;--ifm-button-background-color:inherit;--ifm-button-border-color:var(--ifm-button-background-color);--ifm-button-border-width:var(--ifm-global-border-width);--ifm-button-font-weight:var(--ifm-font-weight-bold);--ifm-button-padding-horizontal:1.5rem;--ifm-button-padding-vertical:0.375rem;--ifm-button-size-multiplier:1;--ifm-button-transition-duration:var(--ifm-transition-fast);--ifm-button-border-radius:calc(var(--ifm-global-radius)*var(--ifm-button-size-multiplier));--ifm-button-group-margin:2px;--ifm-card-background-color:var(--ifm-background-surface-color);--ifm-card-border-radius:calc(var(--ifm-global-radius)*2);--ifm-card-horizontal-spacing:var(--ifm-global-spacing);--ifm-card-vertical-spacing:var(--ifm-global-spacing);--ifm-toc-border-color:var(--ifm-color-emphasis-300);--ifm-toc-link-color:var(--ifm-color-content-secondary);--ifm-toc-padding-vertical:0.5rem;--ifm-toc-padding-horizontal:0.5rem;--ifm-dropdown-background-color:var(--ifm-background-surface-color);--ifm-dropdown-font-weight:var(--ifm-font-weight-semibold);--ifm-dropdown-link-color:var(--ifm-font-color-base);--ifm-dropdown-hover-background-color:var(--ifm-hover-overlay);--ifm-footer-background-color:var(--ifm-color-emphasis-100);--ifm-footer-color:inherit;--ifm-footer-link-color:var(--ifm-color-emphasis-700);--ifm-footer-link-hover-color:var(--ifm-color-primary);--ifm-footer-link-horizontal-spacing:0.5rem;--ifm-footer-padding-horizontal:calc(var(--ifm-spacing-horizontal)*2);--ifm-footer-padding-vertical:calc(var(--ifm-spacing-vertical)*2);--ifm-footer-title-color:inherit;--ifm-hero-background-color:var(--ifm-background-surface-color);--ifm-hero-text-color:var(--ifm-color-emphasis-800);--ifm-menu-color:var(--ifm-color-emphasis-700);--ifm-menu-color-active:var(--ifm-color-primary);--ifm-menu-color-background-active:var(--ifm-hover-overlay);--ifm-menu-color-background-hover:var(--ifm-hover-overlay);--ifm-menu-link-padding-horizontal:1rem;--ifm-menu-link-padding-vertical:0.375rem;--ifm-menu-link-sublist-icon:url('data:image/svg+xml;utf8,');--ifm-menu-link-sublist-icon-filter:none;--ifm-navbar-background-color:var(--ifm-background-surface-color);--ifm-navbar-height:3.75rem;--ifm-navbar-item-padding-horizontal:0.75rem;--ifm-navbar-item-padding-vertical:0.25rem;--ifm-navbar-link-color:var(--ifm-font-color-base);--ifm-navbar-link-active-color:var(--ifm-link-color);--ifm-navbar-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-navbar-padding-vertical:calc(var(--ifm-spacing-vertical)*0.5);--ifm-navbar-shadow:var(--ifm-global-shadow-lw);--ifm-navbar-search-input-background-color:var(--ifm-color-emphasis-200);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-800);--ifm-navbar-search-input-placeholder-color:var(--ifm-color-emphasis-500);--ifm-navbar-search-input-icon:url('data:image/svg+xml;utf8,');--ifm-navbar-sidebar-width:83vw;--ifm-pagination-border-radius:calc(var(--ifm-global-radius)*var(--ifm-pagination-size-multiplier));--ifm-pagination-color-active:var(--ifm-color-primary);--ifm-pagination-font-size:1rem;--ifm-pagination-item-active-background:var(--ifm-hover-overlay);--ifm-pagination-page-spacing:0.0625rem;--ifm-pagination-padding-horizontal:calc(var(--ifm-spacing-horizontal)*1);--ifm-pagination-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-pagination-size-multiplier:1;--ifm-pagination-nav-border-radius:var(--ifm-global-radius);--ifm-pagination-nav-color-hover:var(--ifm-color-primary);--ifm-pills-color-active:var(--ifm-color-primary);--ifm-pills-color-background-active:var(--ifm-hover-overlay);--ifm-pills-spacing:0.0625rem;--ifm-tabs-color:var(--ifm-font-color-secondary);--ifm-tabs-color-active:var(--ifm-color-primary);--ifm-tabs-color-active-border:var(--ifm-tabs-color-active);--ifm-tabs-padding-horizontal:1rem;--ifm-tabs-padding-vertical:1rem;--ifm-tabs-spacing:0.0625rem;--ifm-color-primary:#1b558c;--ifm-color-primary-dark:#103252;--ifm-color-primary-darker:#0d2c48;--ifm-color-primary-darkest:#0a2135;--ifm-color-primary-light:#2984d8;--ifm-color-primary-lighter:#388ad7;--ifm-color-primary-lightest:#59a6ee;--ifm-code-font-size:95%;--docusaurus-announcement-bar-height:auto;--collapse-button-bg-color-dark:#2e333a;--docsearch-primary-color:#5468ff;--docsearch-text-color:#1c1e21;--docsearch-spacing:12px;--docsearch-icon-stroke-width:1.4;--docsearch-highlight-color:var(--docsearch-primary-color);--docsearch-muted-color:#969faf;--docsearch-container-background:rgba(101,108,133,.8);--docsearch-logo-color:#5468ff;--docsearch-modal-width:560px;--docsearch-modal-height:600px;--docsearch-modal-background:#f5f6f7;--docsearch-modal-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.5),0 3px 8px 0 #555a64;--docsearch-searchbox-height:56px;--docsearch-searchbox-background:#ebedf0;--docsearch-searchbox-focus-background:#fff;--docsearch-searchbox-shadow:inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-hit-height:56px;--docsearch-hit-color:#444950;--docsearch-hit-active-color:#fff;--docsearch-hit-background:#fff;--docsearch-hit-shadow:0 1px 3px 0 #d4d9e1;--docsearch-key-gradient:linear-gradient(-225deg,#d5dbe4,#f8f8f8);--docsearch-key-shadow:inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(30,35,90,.4);--docsearch-footer-height:44px;--docsearch-footer-background:#fff;--docsearch-footer-shadow:0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69,98,155,.12);--docsearch-primary-color:var(--ifm-color-primary);--docsearch-text-color:var(--ifm-font-color-base);--docusaurus-tag-list-border:var(--ifm-color-emphasis-300);--doc-sidebar-width:300px;--doc-sidebar-hidden-width:30px}.badge--danger,.badge--info,.badge--primary,.badge--secondary,.badge--success,.badge--warning{--ifm-badge-border-color:var(--ifm-badge-background-color)}.button--link,.button--outline{--ifm-button-background-color:transparent}html{-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--ifm-background-color);color:var(--ifm-font-color-base);color-scheme:var(--ifm-color-scheme);font:var(--ifm-font-size-base)/var(--ifm-line-height-base) var(--ifm-font-family-base)}.playgroundHeader_nN\+J,code{font-size:var(--ifm-code-font-size)}iframe{border:0;color-scheme:auto}.container{margin:0 auto;max-width:var(--ifm-container-width)}.padding-bottom--none,.padding-vert--none{padding-bottom:0!important}.padding-top--none,.padding-vert--none{padding-top:0!important}.padding-horiz--none,.padding-left--none{padding-left:0!important}.padding-horiz--none,.padding-right--none{padding-right:0!important}.container--fluid{max-width:inherit}.row{display:flex;flex-direction:row;flex-wrap:wrap;margin:0 calc(var(--ifm-spacing-horizontal)*-1)}.margin-bottom--none,.margin-vert--none,.markdown>:last-child{margin-bottom:0!important}.margin-top--none,.margin-vert--none{margin-top:0!important}.row .col{--ifm-col-width:100%;flex:1 0;margin-left:0}.row .col[class*=col--]{flex:0 0 var(--ifm-col-width);max-width:var(--ifm-col-width)}.row .col.col--1{--ifm-col-width:8.33333%}.row .col.col--offset-1{margin-left:8.33333%}.row .col.col--2{--ifm-col-width:16.66667%}.row .col.col--offset-2{margin-left:16.66667%}.row .col.col--3{--ifm-col-width:25%}.row .col.col--offset-3{margin-left:25%}.row .col.col--4{--ifm-col-width:33.33333%}.row .col.col--offset-4{margin-left:33.33333%}.row .col.col--5{--ifm-col-width:41.66667%}.row .col.col--offset-5{margin-left:41.66667%}.row .col.col--6{--ifm-col-width:50%}.row .col.col--offset-6{margin-left:50%}.row .col.col--7{--ifm-col-width:58.33333%}.row .col.col--offset-7{margin-left:58.33333%}.row .col.col--8{--ifm-col-width:66.66667%}.row .col.col--offset-8{margin-left:66.66667%}.row .col.col--9{--ifm-col-width:75%}.row .col.col--offset-9{margin-left:75%}.row .col.col--10{--ifm-col-width:83.33333%}.row .col.col--offset-10{margin-left:83.33333%}.row .col.col--11{--ifm-col-width:91.66667%}.row .col.col--offset-11{margin-left:91.66667%}.row .col.col--12{--ifm-col-width:100%}.row .col.col--offset-12{margin-left:100%}.row--no-gutters{margin-left:0;margin-right:0}.margin-horiz--none,.margin-left--none{margin-left:0!important}.margin-horiz--none,.margin-right--none{margin-right:0!important}.row--no-gutters>.col{padding-left:0;padding-right:0}.row--align-top{align-items:flex-start}.row--align-bottom{align-items:flex-end}.row--align-center{align-items:center}.row--align-stretch{align-items:stretch}.row--align-baseline{align-items:baseline}.margin--none{margin:0!important}.margin-bottom--xs,.margin-vert--xs{margin-bottom:.25rem!important}.margin-top--xs,.margin-vert--xs{margin-top:.25rem!important}.margin-horiz--xs,.margin-left--xs{margin-left:.25rem!important}.margin-horiz--xs,.margin-right--xs{margin-right:.25rem!important}.margin--xs{margin:.25rem!important}.margin-bottom--sm,.margin-vert--sm{margin-bottom:.5rem!important}.margin-top--sm,.margin-vert--sm{margin-top:.5rem!important}.margin-horiz--sm,.margin-left--sm{margin-left:.5rem!important}.margin-horiz--sm,.margin-right--sm{margin-right:.5rem!important}.margin--sm{margin:.5rem!important}.margin-bottom--md,.margin-vert--md{margin-bottom:1rem!important}.margin-top--md,.margin-vert--md{margin-top:1rem!important}.margin-horiz--md,.margin-left--md{margin-left:1rem!important}.margin-horiz--md,.margin-right--md{margin-right:1rem!important}.margin--md{margin:1rem!important}.margin-bottom--lg,.margin-vert--lg{margin-bottom:2rem!important}.margin-top--lg,.margin-vert--lg{margin-top:2rem!important}.margin-horiz--lg,.margin-left--lg{margin-left:2rem!important}.margin-horiz--lg,.margin-right--lg{margin-right:2rem!important}.margin--lg{margin:2rem!important}.margin-bottom--xl,.margin-vert--xl{margin-bottom:5rem!important}.margin-top--xl,.margin-vert--xl{margin-top:5rem!important}.margin-horiz--xl,.margin-left--xl{margin-left:5rem!important}.margin-horiz--xl,.margin-right--xl{margin-right:5rem!important}.margin--xl{margin:5rem!important}.padding--none{padding:0!important}.padding-bottom--xs,.padding-vert--xs{padding-bottom:.25rem!important}.padding-top--xs,.padding-vert--xs{padding-top:.25rem!important}.padding-horiz--xs,.padding-left--xs{padding-left:.25rem!important}.padding-horiz--xs,.padding-right--xs{padding-right:.25rem!important}.padding--xs{padding:.25rem!important}.padding-bottom--sm,.padding-vert--sm{padding-bottom:.5rem!important}.padding-top--sm,.padding-vert--sm{padding-top:.5rem!important}.padding-horiz--sm,.padding-left--sm{padding-left:.5rem!important}.padding-horiz--sm,.padding-right--sm{padding-right:.5rem!important}.padding--sm{padding:.5rem!important}.padding-bottom--md,.padding-vert--md{padding-bottom:1rem!important}.padding-top--md,.padding-vert--md{padding-top:1rem!important}.padding-horiz--md,.padding-left--md{padding-left:1rem!important}.padding-horiz--md,.padding-right--md{padding-right:1rem!important}.padding--md{padding:1rem!important}.padding-bottom--lg,.padding-vert--lg{padding-bottom:2rem!important}.padding-top--lg,.padding-vert--lg{padding-top:2rem!important}.padding-horiz--lg,.padding-left--lg{padding-left:2rem!important}.padding-horiz--lg,.padding-right--lg{padding-right:2rem!important}.padding--lg{padding:2rem!important}.padding-bottom--xl,.padding-vert--xl{padding-bottom:5rem!important}.padding-top--xl,.padding-vert--xl{padding-top:5rem!important}.padding-horiz--xl,.padding-left--xl{padding-left:5rem!important}.padding-horiz--xl,.padding-right--xl{padding-right:5rem!important}.padding--xl{padding:5rem!important}code{background-color:var(--ifm-code-background);border:.1rem solid rgba(0,0,0,.1);border-radius:var(--ifm-code-border-radius);font-family:var(--ifm-font-family-monospace);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal)}a code{color:inherit}pre{background-color:var(--ifm-pre-background);border-radius:var(--ifm-pre-border-radius);color:var(--ifm-pre-color);font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace);padding:var(--ifm-pre-padding)}pre code{background-color:transparent;border:none;font-size:100%;line-height:inherit;padding:0}kbd{background-color:var(--ifm-color-emphasis-0);border:1px solid var(--ifm-color-emphasis-400);border-radius:.2rem;box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-400);color:var(--ifm-color-emphasis-800);font:80% var(--ifm-font-family-monospace);padding:.15rem .3rem}h1,h2,h3,h4,h5,h6{color:var(--ifm-heading-color);font-family:var(--ifm-heading-font-family);font-weight:var(--ifm-heading-font-weight);line-height:var(--ifm-heading-line-height);margin:var(--ifm-heading-margin-top) 0 var(--ifm-heading-margin-bottom) 0}h1{font-size:var(--ifm-h1-font-size)}h2{font-size:var(--ifm-h2-font-size)}h3{font-size:var(--ifm-h3-font-size)}h4{font-size:var(--ifm-h4-font-size)}h5{font-size:var(--ifm-h5-font-size)}h6{font-size:var(--ifm-h6-font-size)}img[align=right]{padding-left:var(--image-alignment-padding)}img[align=left]{padding-right:var(--image-alignment-padding)}.markdown{--ifm-h1-vertical-rhythm-top:3;--ifm-h2-vertical-rhythm-top:2;--ifm-h3-vertical-rhythm-top:1.5;--ifm-heading-vertical-rhythm-top:1.25;--ifm-h1-vertical-rhythm-bottom:1.25;--ifm-heading-vertical-rhythm-bottom:1}.markdown:after,.markdown:before{content:"";display:table}.markdown:after{clear:both}.markdown h1:first-child{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-h1-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown>h2{--ifm-h2-font-size:2rem;margin-top:calc(var(--ifm-h2-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h3{--ifm-h3-font-size:1.5rem;margin-top:calc(var(--ifm-h3-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h4,.markdown>h5,.markdown>h6{margin-top:calc(var(--ifm-heading-vertical-rhythm-top)*var(--ifm-leading))}.markdown>p,.markdown>pre,.markdown>ul{margin-bottom:var(--ifm-leading)}.markdown li>p{margin-top:var(--ifm-list-paragraph-margin)}.markdown li+li{margin-top:var(--ifm-list-item-margin)}ol,ul{margin:0 0 var(--ifm-list-margin);padding-left:var(--ifm-list-left-padding)}ol ol,ul ol{list-style-type:lower-roman}ol ol ol,ol ul ol,ul ol ol,ul ul ol{list-style-type:lower-alpha}table{border-collapse:collapse;display:block;margin-bottom:var(--ifm-spacing-vertical)}table thead tr{border-bottom:2px solid var(--ifm-table-border-color)}table tr{background-color:var(--ifm-table-background);border-top:var(--ifm-table-border-width) solid var(--ifm-table-border-color)}table tr:nth-child(2n){background-color:var(--ifm-table-stripe-background)}table td,table th{border:var(--ifm-table-border-width) solid var(--ifm-table-border-color);padding:var(--ifm-table-cell-padding)}table th{background-color:var(--ifm-table-head-background);color:var(--ifm-table-head-color);font-weight:var(--ifm-table-head-font-weight)}table td{color:var(--ifm-table-cell-color)}strong{font-weight:var(--ifm-font-weight-bold)}a{color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}a:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.breadcrumbs__link:hover,.button:hover,.text--no-decoration,.text--no-decoration:hover,a:not([href]){text-decoration:none}p{margin:0 0 var(--ifm-paragraph-margin-bottom)}blockquote{border-left-width:0;border:0 solid var(--ifm-blockquote-border-color);border-left:var(--ifm-blockquote-border-left-width) solid var(--ifm-blockquote-border-color);box-shadow:var(--ifm-blockquote-shadow);color:var(--ifm-blockquote-color);font-size:var(--ifm-blockquote-font-size);padding:var(--ifm-blockquote-padding-vertical) var(--ifm-blockquote-padding-horizontal)}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{border:var(--ifm-hr-border-width) solid var(--ifm-hr-border-color);margin:var(--ifm-hr-margin-vertical) 0}.shadow--lw{box-shadow:var(--ifm-global-shadow-lw)!important}.shadow--md{box-shadow:var(--ifm-global-shadow-md)!important}.shadow--tl{box-shadow:var(--ifm-global-shadow-tl)!important}.text--primary{color:var(--ifm-color-primary)}.text--secondary{color:var(--ifm-color-secondary)}.text--success{color:var(--ifm-color-success)}.text--info{color:var(--ifm-color-info)}.text--warning{color:var(--ifm-color-warning)}.text--danger{color:var(--ifm-color-danger)}.text--center{text-align:center}.text--left{text-align:left}.text--justify{text-align:justify}.text--right{text-align:right}.text--capitalize{text-transform:capitalize}.text--lowercase{text-transform:lowercase}.admonition h5,.alert__heading,.playgroundHeader_nN\+J,.text--uppercase{text-transform:uppercase}.text--light{font-weight:var(--ifm-font-weight-light)}.text--normal{font-weight:var(--ifm-font-weight-normal)}.text--semibold{font-weight:var(--ifm-font-weight-semibold)}.text--bold{font-weight:var(--ifm-font-weight-bold)}.text--italic{font-style:italic}.text--break{word-wrap:break-word!important;word-break:break-word!important}.clean-btn{background:none;border:none;color:inherit;cursor:pointer;font-family:inherit;padding:0}.alert,.alert .close{color:var(--ifm-alert-foreground-color)}.alert--primary{--ifm-alert-background-color:var(--ifm-color-primary-contrast-background);--ifm-alert-background-color-highlight:rgba(53,120,229,.15);--ifm-alert-foreground-color:var(--ifm-color-primary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-primary-dark)}.alert--secondary{--ifm-alert-background-color:var(--ifm-color-secondary-contrast-background);--ifm-alert-background-color-highlight:rgba(235,237,240,.15);--ifm-alert-foreground-color:var(--ifm-color-secondary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-secondary-dark)}.alert--success{--ifm-alert-background-color:var(--ifm-color-success-contrast-background);--ifm-alert-background-color-highlight:rgba(0,164,0,.15);--ifm-alert-foreground-color:var(--ifm-color-success-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-success-dark)}.alert--info{--ifm-alert-background-color:var(--ifm-color-info-contrast-background);--ifm-alert-background-color-highlight:rgba(84,199,236,.15);--ifm-alert-foreground-color:var(--ifm-color-info-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-info-dark)}.alert--warning{--ifm-alert-background-color:var(--ifm-color-warning-contrast-background);--ifm-alert-background-color-highlight:rgba(255,186,0,.15);--ifm-alert-foreground-color:var(--ifm-color-warning-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-warning-dark)}.alert--danger{--ifm-alert-background-color:var(--ifm-color-danger-contrast-background);--ifm-alert-background-color-highlight:rgba(250,56,62,.15);--ifm-alert-foreground-color:var(--ifm-color-danger-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-danger-dark)}.alert{--ifm-code-background:var(--ifm-alert-background-color-highlight);--ifm-link-color:var(--ifm-alert-foreground-color);--ifm-link-hover-color:var(--ifm-alert-foreground-color);--ifm-link-decoration:underline;--ifm-tabs-color:var(--ifm-alert-foreground-color);--ifm-tabs-color-active:var(--ifm-alert-foreground-color);--ifm-tabs-color-active-border:var(--ifm-alert-border-color);background-color:var(--ifm-alert-background-color);border-left-width:var(--ifm-alert-border-width);border:var(--ifm-alert-border-width) solid var(--ifm-alert-border-color);border-left:var(--ifm-alert-border-left-width) solid var(--ifm-alert-border-color);border-radius:var(--ifm-alert-border-radius);box-shadow:var(--ifm-alert-shadow);padding:var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal)}.alert__heading{align-items:center;display:flex;font:700 var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.5rem}.alert__icon{display:inline-flex;margin-right:.4em}.alert__icon svg{stroke:var(--ifm-alert-foreground-color);stroke-width:0}.alert .close{margin:calc(var(--ifm-alert-padding-vertical)*-1) calc(var(--ifm-alert-padding-horizontal)*-1) 0 0;opacity:.75}.alert .close:focus,.alert .close:hover{opacity:1}.alert a{-webkit-text-decoration-color:var(--ifm-alert-border-color);text-decoration-color:var(--ifm-alert-border-color)}.alert a:hover{text-decoration-thickness:2px}.avatar,.navbar,.navbar>.container,.navbar>.container-fluid{display:flex}.avatar__photo-link{display:block}.avatar__photo{border-radius:50%;height:var(--ifm-avatar-photo-size-md);width:var(--ifm-avatar-photo-size-md)}.avatar__photo--sm{height:var(--ifm-avatar-photo-size-sm);width:var(--ifm-avatar-photo-size-sm)}.avatar__photo--lg{height:var(--ifm-avatar-photo-size-lg);width:var(--ifm-avatar-photo-size-lg)}.avatar__photo--xl{height:var(--ifm-avatar-photo-size-xl);width:var(--ifm-avatar-photo-size-xl)}.card--full-height,.navbar__logo img,body,html{height:100%}.avatar__photo+.avatar__intro{margin-left:var(--ifm-avatar-intro-margin)}.avatar__intro{display:flex;flex:1 1;flex-direction:column;justify-content:center;text-align:var(--ifm-avatar-intro-alignment)}.badge,.breadcrumbs__item,.breadcrumbs__link,.button,.dropdown>.navbar__link:after{display:inline-block}.avatar__name{font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base)}.avatar__subtitle{margin-top:.25rem}.avatar--vertical{--ifm-avatar-intro-alignment:center;--ifm-avatar-intro-margin:0.5rem;align-items:center;flex-direction:column}.avatar--vertical .avatar__intro{margin-left:0}.badge{background-color:var(--ifm-badge-background-color);border:var(--ifm-badge-border-width) solid var(--ifm-badge-border-color);border-radius:var(--ifm-badge-border-radius);color:var(--ifm-badge-color);font-size:75%;font-weight:var(--ifm-font-weight-bold);line-height:1;padding:var(--ifm-badge-padding-vertical) var(--ifm-badge-padding-horizontal)}.badge--primary{--ifm-badge-background-color:var(--ifm-color-primary)}.badge--secondary{--ifm-badge-background-color:var(--ifm-color-secondary);color:var(--ifm-color-black)}.badge--success{--ifm-badge-background-color:var(--ifm-color-success)}.badge--info{--ifm-badge-background-color:var(--ifm-color-info)}.badge--warning{--ifm-badge-background-color:var(--ifm-color-warning)}.badge--danger{--ifm-badge-background-color:var(--ifm-color-danger)}.breadcrumbs{margin-bottom:0;padding-left:0}.breadcrumbs__item:not(:first-child){margin-left:var(--ifm-breadcrumb-spacing)}.breadcrumbs__item:not(:last-child){margin-right:var(--ifm-breadcrumb-spacing)}.breadcrumbs__item:not(:last-child):after{background:var(--ifm-breadcrumb-separator) center;content:" ";display:inline-block;filter:var(--ifm-breadcrumb-separator-filter);height:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier));margin:0 .5rem;opacity:.5;width:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier))}.breadcrumbs__item--active .breadcrumbs__link{color:var(--ifm-breadcrumb-color-active)}.breadcrumbs__item--active .breadcrumbs__link,.breadcrumbs__item:not(.breadcrumbs__item--active):hover .breadcrumbs__link{background:var(--ifm-breadcrumb-item-background-active)}.breadcrumbs__link{border-radius:var(--ifm-breadcrumb-border-radius);color:var(--ifm-font-color-base);font-size:calc(1rem*var(--ifm-breadcrumb-size-multiplier));padding:calc(var(--ifm-breadcrumb-padding-vertical)*var(--ifm-breadcrumb-size-multiplier)) calc(var(--ifm-breadcrumb-padding-horizontal)*var(--ifm-breadcrumb-size-multiplier));transition-duration:var(--ifm-transition-fast);transition-property:background,color}.breadcrumbs--sm{--ifm-breadcrumb-size-multiplier:0.8}.breadcrumbs--lg{--ifm-breadcrumb-size-multiplier:1.2}.button{background-color:var(--ifm-button-background-color);border:var(--ifm-button-border-width) solid var(--ifm-button-border-color);border-radius:var(--ifm-button-border-radius);cursor:pointer;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:var(--ifm-button-font-weight);line-height:1.5;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition-duration:var(--ifm-button-transition-duration);transition-property:color,background,border-color;user-select:none}.button,.button:hover{color:var(--ifm-button-color)}.button--outline{--ifm-button-color:var(--ifm-button-border-color)}.button--outline:hover{--ifm-button-background-color:var(--ifm-button-border-color)}.button--link{--ifm-button-border-color:transparent;color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}.button--link.button--active,.button--link:active,.button--link:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button.disabled,.button:disabled,.button[disabled]{opacity:.65;pointer-events:none}.button--sm{--ifm-button-size-multiplier:0.8}.button--lg{--ifm-button-size-multiplier:1.35}.button--block{display:block;width:100%}.button.button--secondary{color:var(--ifm-color-gray-900)}.button.button--secondary.button--outline:not(.button--active):not(:hover),.sidebarItemLink_zyXk{color:var(--ifm-font-color-base)}.button--primary{--ifm-button-border-color:var(--ifm-color-primary)}.button--primary:not(.button--outline){--ifm-button-background-color:var(--ifm-color-primary)}.button--primary:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-primary-dark);--ifm-button-border-color:var(--ifm-color-primary-dark)}.button--primary.button--active,.button--primary:active{--ifm-button-border-color:var(--ifm-color-primary-darker);--ifm-button-background-color:var(--ifm-color-primary-darker);background-color:var(--ifm-color-primary-darker);border-color:var(--ifm-color-primary-darker)}.button--secondary{--ifm-button-border-color:var(--ifm-color-secondary)}.button--secondary:not(.button--outline){--ifm-button-background-color:var(--ifm-color-secondary)}.button--secondary:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-secondary-dark);--ifm-button-border-color:var(--ifm-color-secondary-dark)}.button--secondary.button--active,.button--secondary:active{--ifm-button-border-color:var(--ifm-color-secondary-darker);--ifm-button-background-color:var(--ifm-color-secondary-darker);background-color:var(--ifm-color-secondary-darker);border-color:var(--ifm-color-secondary-darker)}.button--success{--ifm-button-border-color:var(--ifm-color-success)}.button--success:not(.button--outline){--ifm-button-background-color:var(--ifm-color-success)}.button--success:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-success-dark);--ifm-button-border-color:var(--ifm-color-success-dark)}.button--success.button--active,.button--success:active{--ifm-button-border-color:var(--ifm-color-success-darker);--ifm-button-background-color:var(--ifm-color-success-darker);background-color:var(--ifm-color-success-darker);border-color:var(--ifm-color-success-darker)}.button--info{--ifm-button-border-color:var(--ifm-color-info)}.button--info:not(.button--outline){--ifm-button-background-color:var(--ifm-color-info)}.button--info:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-info-dark);--ifm-button-border-color:var(--ifm-color-info-dark)}.button--info.button--active,.button--info:active{--ifm-button-border-color:var(--ifm-color-info-darker);--ifm-button-background-color:var(--ifm-color-info-darker);background-color:var(--ifm-color-info-darker);border-color:var(--ifm-color-info-darker)}.button--warning{--ifm-button-border-color:var(--ifm-color-warning)}.button--warning:not(.button--outline){--ifm-button-background-color:var(--ifm-color-warning)}.button--warning:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-warning-dark);--ifm-button-border-color:var(--ifm-color-warning-dark)}.button--warning.button--active,.button--warning:active{--ifm-button-border-color:var(--ifm-color-warning-darker);--ifm-button-background-color:var(--ifm-color-warning-darker);background-color:var(--ifm-color-warning-darker);border-color:var(--ifm-color-warning-darker)}.button--danger{--ifm-button-border-color:var(--ifm-color-danger)}.button--danger:not(.button--outline){--ifm-button-background-color:var(--ifm-color-danger)}.button--danger:not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-danger-dark);--ifm-button-border-color:var(--ifm-color-danger-dark)}.button--danger.button--active,.button--danger:active{--ifm-button-border-color:var(--ifm-color-danger-darker);--ifm-button-background-color:var(--ifm-color-danger-darker);background-color:var(--ifm-color-danger-darker);border-color:var(--ifm-color-danger-darker)}.button-group{display:inline-flex}.button-group>.button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0;margin-left:var(--ifm-button-group-margin)}.button-group>.button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.button-group>.button--active{z-index:1}.button-group--block{display:flex;justify-content:stretch}.button-group--block>.button{flex-grow:1}.card{background-color:var(--ifm-card-background-color);border-radius:var(--ifm-card-border-radius);box-shadow:var(--ifm-global-shadow-lw);display:flex;flex-direction:column}.card__image{padding-top:var(--ifm-card-vertical-spacing)}.card__image:first-child{padding-top:0}.card__body,.card__footer,.card__header{padding:var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing)}.card__body:not(:last-child),.card__footer:not(:last-child),.card__header:not(:last-child){padding-bottom:0}.admonition-content>:last-child,.card__body>:last-child,.card__footer>:last-child,.card__header>:last-child{margin-bottom:0}.card__footer{margin-top:auto}.table-of-contents{font-size:.8rem;margin-bottom:0;padding:var(--ifm-toc-padding-vertical) 0}.table-of-contents,.table-of-contents ul{list-style:none;padding-left:var(--ifm-toc-padding-horizontal)}.table-of-contents li{margin:var(--ifm-toc-padding-vertical) var(--ifm-toc-padding-horizontal)}.table-of-contents__left-border{border-left:1px solid var(--ifm-toc-border-color)}.table-of-contents__link{color:var(--ifm-toc-link-color)}.table-of-contents__link--active,.table-of-contents__link--active code,.table-of-contents__link:hover,.table-of-contents__link:hover code{color:var(--ifm-color-primary);text-decoration:none}.close{color:var(--ifm-color-black);float:right;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);line-height:1;opacity:.5;padding:1rem;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.close:hover{opacity:.7}.close:focus{opacity:.8}.dropdown{display:inline-flex;font-weight:var(--ifm-dropdown-font-weight);position:relative;vertical-align:top}.dropdown--hoverable:hover .dropdown__menu,.dropdown--show .dropdown__menu{opacity:1;transform:translateY(0);visibility:visible}.dropdown--right .dropdown__menu{left:inherit;right:0}.dropdown--nocaret .navbar__link:after{content:none!important}.dropdown__menu{background-color:var(--ifm-dropdown-background-color);border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-md);left:0;list-style:none;max-height:80vh;min-width:10rem;opacity:0;overflow-y:auto;padding:.5rem;position:absolute;top:calc(100% - var(--ifm-navbar-item-padding-vertical) + .3rem);transform:translateY(-.625rem);transition-duration:var(--ifm-transition-fast);transition-property:opacity,transform,visibility;transition-timing-function:var(--ifm-transition-timing-default);visibility:hidden;z-index:var(--ifm-z-index-dropdown)}.menu__caret,.menu__link,.menu__list-item-collapsible{border-radius:.25rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.dropdown__link{border-radius:.25rem;color:var(--ifm-dropdown-link-color);display:block;font-size:.875rem;margin-top:.2rem;padding:.25rem .5rem}.dropdown__link--active,.dropdown__link:hover{background-color:var(--ifm-dropdown-hover-background-color);color:var(--ifm-dropdown-link-color);text-decoration:none}.dropdown__link--active,.dropdown__link--active:hover{--ifm-dropdown-link-color:var(--ifm-link-color)}.dropdown>.navbar__link:after{border-color:currentcolor transparent;border-style:solid;border-width:.4em .4em 0;content:"";margin-left:.3em;position:relative;top:2px;transform:translateY(-50%)}.footer{background-color:var(--ifm-footer-background-color);color:var(--ifm-footer-color);padding:var(--ifm-footer-padding-vertical) var(--ifm-footer-padding-horizontal)}.footer--dark{--ifm-footer-background-color:#303846;--ifm-footer-color:var(--ifm-footer-link-color);--ifm-footer-link-color:var(--ifm-color-secondary);--ifm-footer-title-color:var(--ifm-color-white)}.footer__links{margin-bottom:1rem}.footer__link-item{color:var(--ifm-footer-link-color);line-height:2}.footer__link-item:hover{color:var(--ifm-footer-link-hover-color)}.footer__link-separator{margin:0 var(--ifm-footer-link-horizontal-spacing)}.footer__logo{margin-top:1rem;max-width:10rem}.footer__title{color:var(--ifm-footer-title-color);font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base);margin-bottom:var(--ifm-heading-margin-bottom)}.menu,.navbar__link{font-weight:var(--ifm-font-weight-semibold)}.footer__item{margin-top:0}.footer__items{list-style:none;margin-bottom:0;padding-left:0}[type=checkbox]{padding:0}.hero{align-items:center;background-color:var(--ifm-hero-background-color);color:var(--ifm-hero-text-color);display:flex;padding:4rem 2rem}.hero--primary{--ifm-hero-background-color:var(--ifm-color-primary);--ifm-hero-text-color:var(--ifm-font-color-base-inverse)}.hero--dark{--ifm-hero-background-color:#303846;--ifm-hero-text-color:var(--ifm-color-white)}.blogPostTitle_d4p0,.hero__title{font-size:3rem}.hero__subtitle{font-size:1.5rem}.menu__list{list-style:none;margin:0;padding-left:0}.menu__caret,.menu__link{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu__list .menu__list{flex:0 0 100%;margin-top:.25rem;padding-left:var(--ifm-menu-link-padding-horizontal)}.menu__list-item:not(:first-child){margin-top:.25rem}.menu__list-item--collapsed .menu__list{height:0;overflow:hidden}.details_Q743[data-collapsed=false].isBrowser_rWTL>summary:before,.details_Q743[open]:not(.isBrowser_rWTL)>summary:before,.menu__list-item--collapsed .menu__caret:before,.menu__list-item--collapsed .menu__link--sublist:after{transform:rotate(90deg)}.menu__list-item-collapsible{display:flex;flex-wrap:wrap;position:relative}.menu__caret:hover,.menu__link:hover,.menu__list-item-collapsible--active,.menu__list-item-collapsible:hover{background:var(--ifm-menu-color-background-hover)}.menu__list-item-collapsible .menu__link--active,.menu__list-item-collapsible .menu__link:hover{background:none!important}.menu__caret,.menu__link{display:flex}.menu__link{color:var(--ifm-menu-color);flex:1;justify-content:space-between;line-height:1.25}.menu__link:hover{color:var(--ifm-menu-color);text-decoration:none}.menu__caret:before,.menu__link--sublist:after{height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast) linear;width:1.25rem;content:"";filter:var(--ifm-menu-link-sublist-icon-filter)}.menu__link--sublist:after{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;min-width:1.25rem}.menu__link--active,.menu__link--active:hover{color:var(--ifm-menu-color-active)}.navbar__brand,.navbar__link{color:var(--ifm-navbar-link-color)}.menu__link--active:not(.menu__link--sublist){background:var(--ifm-menu-color-background-active)}.menu__caret{margin-left:.1rem}.menu__caret:before{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem}.navbar--dark,html[data-theme=dark]{--ifm-menu-link-sublist-icon-filter:invert(100%) sepia(94%) saturate(17%) hue-rotate(223deg) brightness(104%) contrast(98%)}.navbar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-navbar-shadow);height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar--fixed-top{position:-webkit-sticky;position:sticky;top:0;z-index:var(--ifm-z-index-fixed)}.navbar-sidebar,.navbar-sidebar__backdrop{bottom:0;opacity:0;position:fixed;transition-timing-function:ease-in-out;visibility:hidden;top:0;left:0}.navbar__inner{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%}.navbar__brand{align-items:center;display:flex;margin-right:1rem;min-width:0}.navbar__brand:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar__title{flex:1 1 auto;overflow:hidden}.navbar__toggle{display:none;margin-right:.5rem}.navbar__logo{flex:0 0 auto;height:2rem;margin-right:.5rem}.navbar__items{align-items:center;display:flex;flex:1;min-width:0}.navbar__items--center{flex:0 0 auto}.navbar__items--center .navbar__brand{margin:0}.navbar__items--center+.navbar__items--right{flex:1}.navbar__items--right{flex:0 0 auto;justify-content:flex-end}.navbar__items--right>:last-child{padding-right:0}.navbar__item{display:inline-block;padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}#nprogress,.navbar__item.dropdown .navbar__link:not([href]){pointer-events:none}.navbar__link--active,.navbar__link:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar--dark,.navbar--primary{--ifm-menu-color:var(--ifm-color-gray-300);--ifm-navbar-link-color:var(--ifm-color-gray-100);--ifm-navbar-search-input-background-color:hsla(0,0%,100%,.1);--ifm-navbar-search-input-placeholder-color:hsla(0,0%,100%,.5);color:var(--ifm-color-white)}.navbar--dark{--ifm-navbar-background-color:#242526;--ifm-menu-color-background-active:hsla(0,0%,100%,.05);--ifm-navbar-search-input-color:var(--ifm-color-white)}.navbar--primary{--ifm-navbar-background-color:var(--ifm-color-primary);--ifm-navbar-link-hover-color:var(--ifm-color-white);--ifm-menu-color-active:var(--ifm-color-white);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-500)}.navbar__search-input{-webkit-appearance:none;appearance:none;background:var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat .75rem center/1rem 1rem;border:none;border-radius:2rem;color:var(--ifm-navbar-search-input-color);cursor:text;display:inline-block;font-size:.9rem;height:2rem;padding:0 .5rem 0 2.25rem;width:12.5rem}.navbar__search-input:-ms-input-placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar__search-input::placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar-sidebar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-global-shadow-md);transform:translate3d(-100%,0,0);transition-duration:.25s;transition-property:opacity,visibility,transform;width:var(--ifm-navbar-sidebar-width)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar__items{transform:translateZ(0)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.navbar-sidebar__backdrop{background-color:rgba(0,0,0,.6);right:0;transition-duration:.1s;transition-property:opacity,visibility}.navbar-sidebar__brand{align-items:center;box-shadow:var(--ifm-navbar-shadow);display:flex;flex:1;height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar-sidebar__items{display:flex;height:calc(100% - var(--ifm-navbar-height));transition:transform var(--ifm-transition-fast) ease-in-out}.navbar-sidebar__items--show-secondary{transform:translate3d(calc(var(--ifm-navbar-sidebar-width)*-1),0,0)}.navbar-sidebar__item{flex-shrink:0;padding:.5rem;width:calc(var(--ifm-navbar-sidebar-width))}.navbar-sidebar__back{background:var(--ifm-menu-color-background-active);font-size:15px;font-weight:var(--ifm-button-font-weight);margin:0 0 .2rem -.5rem;padding:.6rem 1.5rem;position:relative;text-align:left;top:-.5rem;width:calc(100% + 1rem)}.navbar-sidebar__close{display:flex;margin-left:auto}.pagination__item,.pagination__link{display:inline-block}.pagination{font-size:var(--ifm-pagination-font-size);padding-left:0}.pagination--sm{--ifm-pagination-font-size:0.8rem;--ifm-pagination-padding-horizontal:0.8rem;--ifm-pagination-padding-vertical:0.2rem}.pagination--lg{--ifm-pagination-font-size:1.2rem;--ifm-pagination-padding-horizontal:1.2rem;--ifm-pagination-padding-vertical:0.3rem}.pagination__item:not(:first-child){margin-left:var(--ifm-pagination-page-spacing)}.pagination__item:not(:last-child){margin-right:var(--ifm-pagination-page-spacing)}.pagination__item>span{padding:var(--ifm-pagination-padding-vertical)}.pagination__item--active .pagination__link{color:var(--ifm-pagination-color-active)}.pagination__item--active .pagination__link,.pagination__item:not(.pagination__item--active):hover .pagination__link{background:var(--ifm-pagination-item-active-background)}.pagination__item--disabled,.pagination__item[disabled]{opacity:.25;pointer-events:none}.pagination__link{border-radius:var(--ifm-pagination-border-radius);color:var(--ifm-font-color-base);padding:var(--ifm-pagination-padding-vertical) var(--ifm-pagination-padding-horizontal);transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination__link:hover,.sidebarItemLink_zyXk:hover{text-decoration:none}.pagination-nav{display:flex}.pagination-nav__item{display:flex;flex:1 50%;max-width:50%}.pagination-nav__item--next{text-align:right}.pagination-nav__item+.pagination-nav__item{margin-left:var(--ifm-spacing-horizontal)}.pagination-nav__link{border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-pagination-nav-border-radius);flex-grow:1;line-height:var(--ifm-heading-line-height);padding:var(--ifm-global-spacing);transition:border-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav__link:hover{border-color:var(--ifm-pagination-nav-color-hover);text-decoration:none}.pagination-nav__label{font-size:var(--ifm-h4-font-size);font-weight:var(--ifm-heading-font-weight);word-break:break-word}.pagination-nav__item:first-child .pagination-nav__label:before{content:"« "}.pagination-nav__item--next .pagination-nav__label:after{content:" »"}.pagination-nav__sublabel{color:var(--ifm-color-content-secondary);font-size:var(--ifm-h5-font-size);font-weight:var(--ifm-font-weight-semibold);margin-bottom:.25rem}.pills,.sidebarItemTitle_9G5K,.sidebarMenuCloseIcon_6kU2,.tabs{font-weight:var(--ifm-font-weight-bold)}.pills{padding-left:0}.pills__item{border-radius:.5rem;cursor:pointer;display:inline-block;padding:.25rem 1rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pills__item--active{background:var(--ifm-pills-color-background-active);color:var(--ifm-pills-color-active)}.pills__item:not(.pills__item--active):hover{background-color:var(--ifm-pills-color-background-active)}.pills__item:not(:first-child){margin-left:var(--ifm-pills-spacing)}.pills__item:not(:last-child){margin-right:var(--ifm-pills-spacing)}.docItemContainer_oiyr article>:first-child,.docItemContainer_oiyr header+*,.pills__item+.pills__item{margin-top:0}.pills--block{display:flex;justify-content:stretch}.pills--block .pills__item{flex-grow:1;text-align:center}.tabs{color:var(--ifm-tabs-color);display:flex;margin-bottom:0;overflow-x:auto;padding-left:0}.tabs__item{border-bottom:3px solid transparent;border-radius:var(--ifm-global-radius);cursor:pointer;display:inline-flex;padding:var(--ifm-tabs-padding-vertical) var(--ifm-tabs-padding-horizontal);transition:background-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs__item--active{border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.tabs__item:hover{background-color:var(--ifm-hover-overlay)}.tabs--block{justify-content:stretch}.tabs--block .tabs__item{flex-grow:1;justify-content:center}html[data-theme=dark]{--ifm-color-scheme:dark;--ifm-color-emphasis-0:var(--ifm-color-gray-1000);--ifm-color-emphasis-100:var(--ifm-color-gray-900);--ifm-color-emphasis-200:var(--ifm-color-gray-800);--ifm-color-emphasis-300:var(--ifm-color-gray-700);--ifm-color-emphasis-400:var(--ifm-color-gray-600);--ifm-color-emphasis-600:var(--ifm-color-gray-400);--ifm-color-emphasis-700:var(--ifm-color-gray-300);--ifm-color-emphasis-800:var(--ifm-color-gray-200);--ifm-color-emphasis-900:var(--ifm-color-gray-100);--ifm-color-emphasis-1000:var(--ifm-color-gray-0);--ifm-background-color:#18191a;--ifm-background-surface-color:#242526;--ifm-hover-overlay:hsla(0,0%,100%,.05);--ifm-color-content-secondary:#fff;--ifm-breadcrumb-separator-filter:invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(99%) contrast(95%);--ifm-code-background:#333437;--ifm-scrollbar-track-background-color:#444;--ifm-scrollbar-thumb-background-color:#686868;--ifm-scrollbar-thumb-hover-background-color:#7a7a7a;--ifm-color-primary-contrast-background:#102445;--ifm-color-primary-contrast-foreground:#ebf2fc;--ifm-color-secondary-contrast-background:#474748;--ifm-color-secondary-contrast-foreground:#fdfdfe;--ifm-color-success-contrast-background:#003100;--ifm-color-success-contrast-foreground:#e6f6e6;--ifm-color-info-contrast-background:#193c47;--ifm-color-info-contrast-foreground:#eef9fd;--ifm-color-warning-contrast-background:#4d3800;--ifm-color-warning-contrast-foreground:#fff8e6;--ifm-color-danger-contrast-background:#4b1113;--ifm-color-danger-contrast-foreground:#ffebec;--docsearch-text-color:#f5f6f7;--docsearch-container-background:rgba(9,10,17,.8);--docsearch-modal-background:#15172a;--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;--docsearch-searchbox-background:#090a11;--docsearch-searchbox-focus-background:#000;--docsearch-hit-color:#bec3c9;--docsearch-hit-shadow:none;--docsearch-hit-background:#090a11;--docsearch-key-gradient:linear-gradient(-26.5deg,#565872,#31355b);--docsearch-key-shadow:inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(3,4,9,.3);--docsearch-footer-background:#1e2136;--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73,76,106,.5),0 -4px 8px 0 rgba(0,0,0,.2);--docsearch-logo-color:#fff;--docsearch-muted-color:#7f8497}.admonition h5{margin-bottom:8px;margin-top:0}.admonition-icon{display:inline-block;margin-right:.4em;vertical-align:middle}.admonition-icon svg{stroke-width:0;stroke:var(--ifm-alert-foreground-color);display:inline-block;height:22px;width:22px}.admonition{margin-bottom:1em}.docusaurus-highlight-code-line{background-color:rgba(0,0,0,.1);display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}html[data-theme=dark] .docusaurus-highlight-code-line{background-color:rgba(0,0,0,.3)}#nprogress .bar{background:#29d;height:2px;left:0;position:fixed;top:0;width:100%;z-index:7}#nprogress .peg{box-shadow:0 0 10px #29d,0 0 5px #29d;height:100%;opacity:1;position:absolute;right:0;transform:rotate(3deg) translateY(-4px);width:100px}#docusaurus-base-url-issue-banner-container{display:none}.details_Q743{--docusaurus-details-summary-arrow-size:0.38rem;--docusaurus-details-transition:transform 200ms ease;--docusaurus-details-decoration-color:grey}.details_Q743>summary{cursor:pointer;list-style:none;padding-left:1rem;position:relative}.details_Q743>summary::-webkit-details-marker{display:none}.details_Q743>summary:before{border:var(--docusaurus-details-summary-arrow-size) solid transparent;border-left:var(--docusaurus-details-summary-arrow-size) solid var(--docusaurus-details-decoration-color);content:"";left:0;position:absolute;top:.45rem;transform:rotate(0);transform-origin:calc(var(--docusaurus-details-summary-arrow-size)/2) 50%;transition:var(--docusaurus-details-transition)}.collapsibleContent_K5uX{border-top:1px solid var(--docusaurus-details-decoration-color);margin-top:1rem;padding-top:1rem}.skipToContent_OuoZ{background-color:var(--ifm-background-surface-color);color:var(--ifm-color-emphasis-900);left:100%;padding:calc(var(--ifm-global-spacing)/2) var(--ifm-global-spacing);position:fixed;top:1rem;z-index:calc(var(--ifm-z-index-fixed) + 1)}.skipToContent_OuoZ:focus{box-shadow:var(--ifm-global-shadow-md);left:1rem}.announcementBar_axC9{align-items:center;background-color:var(--ifm-color-white);border-bottom:1px solid var(--ifm-color-emphasis-100);color:var(--ifm-color-black);display:flex;height:var(--docusaurus-announcement-bar-height)}.collapseSidebarButton_eoK2,.sidebarLogo_hmkv,.themedImage_TMUO,html[data-announcement-bar-initially-dismissed=true] .announcementBar_axC9{display:none}.announcementBarPlaceholder_xYHE{flex:0 0 10px}.announcementBarClose_A3A1{align-self:stretch;flex:0 0 30px;line-height:0;padding:0}.announcementBarContent_6uhP{flex:1 1 auto;font-size:85%;padding:5px 0;text-align:center}.announcementBarContent_6uhP a{color:inherit;text-decoration:underline}.toggle_iYfV{cursor:pointer;position:relative;touch-action:pan-x;user-select:none}.DocSearch-Button,.copyButton_M3SB{-webkit-user-select:none;-ms-user-select:none}.toggleScreenReader_h9qa{clip:rect(0 0 0 0);border:0;height:1px;margin:-1px;overflow:hidden;position:absolute;width:1px}.toggleDisabled_xj38{cursor:not-allowed}.toggleTrack_t-f2{background-color:#4d4d4d;border-radius:30px;height:24px;transition:.2s;width:50px}.toggleTrackCheck_mk7D,.toggleTrackX_dm8H{bottom:0;height:10px;margin:auto 0;top:0;position:absolute}.toggleTrackCheck_mk7D{left:8px;opacity:0;transition:opacity .25s;width:14px}.toggleChecked_a04y .toggleTrackCheck_mk7D,.toggleTrackX_dm8H,[data-theme=dark] .toggle_iYfV .toggleTrackCheck_mk7D{opacity:1;transition:opacity .25s}.toggleTrackX_dm8H{right:10px;width:10px}.codeBlockContainer_J\+bg,.playgroundContainer_g1X1{box-shadow:var(--ifm-global-shadow-lw);margin-bottom:var(--ifm-leading)}.toggleChecked_a04y .toggleTrackX_dm8H,[data-theme=dark] .toggle_iYfV .toggleTrackX_dm8H{opacity:0}.toggleTrackThumb_W6To{background-color:#fafafa;border:1px solid #4d4d4d;border-radius:50%;height:22px;left:1px;position:absolute;top:1px;transition:.25s;width:22px}.codeBlockContainer_J\+bg,.codeBlock_rtdJ,.copyButton_M3SB,.playgroundContainer_g1X1{border-radius:var(--ifm-global-radius)}.toggleChecked_a04y .toggleTrackThumb_W6To,[data-theme=dark] .toggle_iYfV .toggleTrackThumb_W6To{left:27px}.toggleFocused_pRSw .toggleTrackThumb_W6To,.toggle_iYfV:hover .toggleTrackThumb_W6To{box-shadow:0 0 2px 3px var(--ifm-color-primary)}.toggle_iYfV:active:not(.toggleDisabled_xj38) .toggleTrackThumb_W6To{box-shadow:0 0 5px 5px var(--ifm-color-primary)}.toggleIcon_pHJ9{align-items:center;display:flex;height:10px;justify-content:center;width:10px}.iconExternalLink_wgqa{margin-left:.3rem;position:relative;top:1px}.iconLanguage_EbrZ{margin-right:5px;vertical-align:text-bottom}html[data-theme=dark] .themedImage--dark_uzRr,html[data-theme=light] .themedImage--light_4Vu1{display:initial}.navbarHideable_RReh{transition:transform var(--ifm-transition-fast) ease}.navbarHidden_FBwS{transform:translate3d(0,calc(-100% - 2px),0)}.navbarSidebarToggle_AVbO{margin-right:1rem}.footerLogoLink_SRtH{opacity:.5;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.codeBlockContent_csEI:hover>.copyButton_M3SB,.codeBlockTitle_oQzk:hover+.codeBlockContent_csEI .copyButton_M3SB,.copyButton_M3SB:focus,.footerLogoLink_SRtH:hover,.hash-link:focus,:hover>.hash-link{opacity:1}body:not(.navigation-with-keyboard) :not(input):focus{outline:0}#__docusaurus{display:flex;flex-direction:column;min-height:100%}.main-wrapper{flex:1 0 auto}.docusaurus-mt-lg{margin-top:3rem}.sidebar_q\+wC{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:-webkit-sticky;position:sticky;top:calc(var(--ifm-navbar-height) + 2rem)}.sidebarItemTitle_9G5K{font-size:var(--ifm-h3-font-size)}.sidebarItemList_6T4b{font-size:.9rem;list-style:none;padding-left:0}.sidebarItem_cjdF{margin-top:.7rem}.sidebarItemLinkActive_wcJs{color:var(--ifm-color-primary)!important}.sidebarMenuIcon_iZzd{vertical-align:middle}.sidebarMenuCloseIcon_6kU2{align-items:center;display:inline-flex;font-size:1.5rem;height:24px;justify-content:center;line-height:.9;width:24px}.playgroundContainer_g1X1{overflow:hidden}.playgroundHeader_nN\+J{background:var(--ifm-color-emphasis-200);color:var(--ifm-color-content);font-weight:700;letter-spacing:.08rem;padding:.75rem}.playgroundHeader_nN\+J:first-of-type{background:var(--ifm-color-emphasis-600);color:var(--ifm-color-content-inverse)}.playgroundEditor_smcs{direction:ltr;font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace)!important}.playgroundPreview_tBjT{background-color:var(--ifm-pre-background);padding:1rem}.codeBlockContent_csEI{direction:ltr;position:relative}.codeBlockTitle_oQzk{border-bottom:1px solid var(--ifm-color-emphasis-300);border-top-left-radius:var(--ifm-global-radius);border-top-right-radius:var(--ifm-global-radius);font-size:var(--ifm-code-font-size);font-weight:500;padding:.75rem var(--ifm-pre-padding)}.codeBlockTitle_oQzk+.codeBlockContent_csEI .codeBlock_rtdJ{border-top-left-radius:0;border-top-right-radius:0}.codeBlock_rtdJ{margin:0;padding:0}.copyButton_M3SB{background:rgba(0,0,0,.3);color:var(--ifm-color-white);opacity:0;padding:.4rem .5rem;position:absolute;right:calc(var(--ifm-pre-padding)/2);top:calc(var(--ifm-pre-padding)/2);transition:opacity .2s ease-in-out;user-select:none}.codeBlockLines_1zSZ{float:left;font:inherit;min-width:100%;padding:var(--ifm-pre-padding)}.heroBanner_etFc{animation:30s infinite a;background:linear-gradient(270deg,#264c98,#1b1f28);background-size:400% 400%;overflow:hidden;padding:4rem 0;position:relative;text-align:center}@keyframes a{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.buttons_\+YzY,.mdxPageWrapper_eQvw{justify-content:center}.DocSearch-Button,.DocSearch-Button-Container,.buttons_\+YzY,.features_n4mZ{align-items:center;display:flex}.features_n4mZ{padding:2rem 0;width:100%}.features_n4mZ .feature_TYIJ{background-color:#fafafa;border-radius:.5rem;margin:1rem 0;padding:1rem}.featureSvg_d3xR{height:200px;width:200px}.searchQueryInput_dLdO,.searchVersionInput_oJeg{background:var(--docsearch-searchbox-focus-background);border:2px solid var(--ifm-toc-border-color);border-radius:var(--ifm-global-radius);color:var(--docsearch-text-color);font:var(--ifm-font-size-base) var(--ifm-font-family-base);margin-bottom:.5rem;padding:.8rem;transition:border var(--ifm-transition-fast) ease;width:100%}.searchQueryInput_dLdO:focus,.searchVersionInput_oJeg:focus{border-color:var(--docsearch-primary-color);outline:0}.searchQueryInput_dLdO:-ms-input-placeholder{color:var(--docsearch-muted-color)}.searchQueryInput_dLdO::placeholder{color:var(--docsearch-muted-color)}.searchResultsColumn_V1kT{font-size:.9rem;font-weight:700}.algoliaLogo_ieE9{max-width:150px}.algoliaLogoPathFill_NLBU{fill:var(--ifm-font-color-base)}.searchResultItem_f0c5{border-bottom:1px solid var(--ifm-toc-border-color);padding:1rem 0}.searchResultItemHeading_59Ih{font-weight:400;margin-bottom:0}.searchResultItemPath_utd2{--ifm-breadcrumb-separator-size-multiplier:1;color:var(--ifm-color-content-secondary);font-size:.8rem}.searchResultItemSummary_EzNh{font-style:italic;margin:.5rem 0 0}.loadingSpinner_CN74{animation:1s linear infinite b;border:.4em solid #eee;border-radius:50%;border-top:.4em solid var(--ifm-color-primary);height:3rem;margin:0 auto;width:3rem}@keyframes b{to{transform:rotate(1turn)}}.loader_-Se\+{margin-top:2rem}.search-result-match{background:rgba(255,215,142,.25);color:var(--docsearch-hit-color);padding:.09em 0}.DocSearch-Button{background:var(--docsearch-searchbox-background);border:0;border-radius:40px;color:var(--docsearch-muted-color);cursor:pointer;font-weight:500;height:36px;justify-content:space-between;padding:0 8px;user-select:none}.DocSearch-Footer,.DocSearch-Help{-webkit-user-select:none;-ms-user-select:none}.DocSearch-Button:active,.DocSearch-Button:focus,.DocSearch-Button:hover{background:var(--docsearch-searchbox-focus-background);box-shadow:var(--docsearch-searchbox-shadow);color:var(--docsearch-text-color);outline:0}.DocSearch-Search-Icon{stroke-width:1.6}.DocSearch-Hit-Tree,.DocSearch-Hit-action,.DocSearch-Hit-icon,.DocSearch-Reset{stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Button .DocSearch-Search-Icon{color:var(--docsearch-text-color)}.DocSearch-Button-Placeholder{font-size:1rem;padding:0 12px 0 6px}.DocSearch-Input,.DocSearch-Link{-webkit-appearance:none;font:inherit}.DocSearch-Button-Keys{display:flex;min-width:calc(40px + .8em)}.DocSearch-Button-Key{align-items:center;background:var(--docsearch-key-gradient);border-radius:3px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);display:flex;height:18px;justify-content:center;margin-right:.4em;padding-bottom:2px;position:relative;top:-1px;width:20px}.DocSearch--active{overflow:hidden!important}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:4}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{appearance:none;background:0 0;border:0;color:var(--docsearch-text-color);flex:1;font-size:1.2em;height:100%;outline:0;padding:0 0 0 8px;width:80%}.DocSearch-Hit-action-button,.DocSearch-Reset{-webkit-appearance:none;border:0;cursor:pointer}.DocSearch-Input:-ms-input-placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Cancel,.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator,.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset{animation:.1s ease-in forwards c;appearance:none;background:none;border-radius:50%;color:var(--docsearch-icon-color);padding:2px;right:0}.DocSearch-Help,.DocSearch-HitsFooter,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Reset:focus{outline:0}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:0 0}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help{font-size:.9em;margin:0;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a,.docs-wrapper{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}.DocSearch-Hit--deleting{opacity:0;transition:.25s linear}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:.25s linear .25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:-webkit-sticky;position:sticky;top:0;z-index:2}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit[aria-selected=true] mark{text-decoration:underline}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{appearance:none;background:none;border-radius:50%;color:inherit;padding:2px}.docSidebarContainer_0YBq,svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon,.tocCollapsibleContent_0dom a{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{-webkit-appearance:none;appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:0;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;user-select:none;width:100%;z-index:5}.DocSearch-Commands li,.DocSearch-Commands-Key{align-items:center;display:flex}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{background:var(--docsearch-key-gradient);border-radius:2px;box-shadow:var(--docsearch-key-shadow);height:18px;justify-content:center;margin-right:.4em;padding-bottom:1px;width:20px}@keyframes c{0%{opacity:0}to{opacity:1}}.DocSearch-Button{margin:0;transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.DocSearch-Container{z-index:calc(var(--ifm-z-index-fixed) + 1)}.iconEdit_mS5F{margin-right:.3em;vertical-align:sub}.tag_WK-t{border:1px solid var(--docusaurus-tag-list-border);transition:border var(--ifm-transition-fast)}.tag_WK-t:hover{--docusaurus-tag-list-border:var(--ifm-link-color);text-decoration:none}.tagRegular_LXbV{border-radius:.5rem;font-size:90%;padding:.3rem .5rem}.tagWithCount_S5Zl{align-items:center;border-left:0;display:flex;padding:0 .5rem 0 1rem;position:relative}.tagWithCount_S5Zl:after,.tagWithCount_S5Zl:before{border:1px solid var(--docusaurus-tag-list-border);content:"";position:absolute;top:50%;transition:inherit}.tagWithCount_S5Zl:before{border-bottom:0;border-right:0;height:1.18rem;right:100%;transform:translate(50%,-50%) rotate(-45deg);width:1.18rem}.tagWithCount_S5Zl:after{border-radius:50%;height:.5rem;left:0;transform:translateY(-50%);width:.5rem}.tagWithCount_S5Zl span{background:var(--ifm-color-secondary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-black);font-size:.7rem;line-height:1.2;margin-left:.3rem;padding:.1rem .4rem}.tag_7kA\+{display:inline-block;margin:.5rem .5rem 0 1rem}.hash-link{opacity:0;padding-left:.5rem;transition:opacity var(--ifm-transition-fast);-webkit-user-select:none;-ms-user-select:none;user-select:none}.hash-link:before{content:"#"}.tags_NBRY{display:inline}.tag_F03v{display:inline-block;margin:0 .4rem .5rem 0}.lastUpdated_mt2f{font-size:smaller;font-style:italic;margin-top:.2rem}.anchorWithStickyNavbar_y2LR{scroll-margin-top:calc(var(--ifm-navbar-height) + .5rem)}.anchorWithHideOnScrollNavbar_3ly5{scroll-margin-top:.5rem}.details_h\+cY{--docusaurus-details-decoration-color:var(--ifm-alert-border-color);--docusaurus-details-transition:transform var(--ifm-transition-fast) ease;border:1px solid var(--ifm-alert-border-color);margin:0 0 var(--ifm-spacing-vertical)}ul.contains-task-list{list-style:none;padding-left:0}.backToTopButton_i9tI{background-color:var(--ifm-color-emphasis-200);border-radius:50%;bottom:1.3rem;box-shadow:var(--ifm-global-shadow-lw);height:3rem;opacity:0;position:fixed;right:1.3rem;transform:scale(0);transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default);width:3rem;z-index:var(--ifm-z-index-fixed)}.backToTopButton_i9tI:after{background-color:var(--ifm-color-emphasis-1000);content:" ";display:inline-block;height:100%;-webkit-mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;width:100%}.backToTopButtonShow_wCmF{opacity:1;transform:scale(1)}.docMainContainer_r8cw,.docPage_lDyR{display:flex;width:100%}.blogPostData_-Im\+{font-size:.9rem}.blogPostDetailsFull_xD8n{flex-direction:column}.image_9q7L{height:100%;object-fit:cover;width:100%}.authorCol_8c0z{flex-grow:1!important;max-width:inherit!important}.tableOfContents_vrFS{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:-webkit-sticky;position:sticky;top:calc(var(--ifm-navbar-height) + 1rem)}.tocCollapsible_aw-L{background-color:var(--ifm-menu-color-background-active);border-radius:var(--ifm-global-radius);margin:1rem 0}.tocCollapsibleButton_zr6a{align-items:center;display:flex;font-size:inherit;justify-content:space-between;padding:.4rem .8rem;width:100%}.tocCollapsibleButton_zr6a:after{background:var(--ifm-menu-link-sublist-icon) 50% 50%/2rem 2rem no-repeat;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast);width:1.25rem}.tocCollapsibleContent_0dom>ul{border-left:none;border-top:1px solid var(--ifm-color-emphasis-300);font-size:15px;padding:.2rem 0}.tocCollapsibleContent_0dom ul li{margin:.4rem .8rem}.tocCollapsibleExpanded_FSiv .tocCollapsibleButton_zr6a:after{transform:none}@media (min-width:997px){:root{--docusaurus-announcement-bar-height:30px}.announcementBarClose_A3A1,.announcementBarPlaceholder_xYHE{flex-basis:50px}.searchBox_Utm0{padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.menuLinkText_OKON{cursor:auto}.menuLinkText_OKON:hover{background:none}.menuLinkText_OKON.hasHref_TwRn{cursor:pointer}.sidebar_a3j0{display:flex;flex-direction:column;height:100%;max-height:100vh;padding-top:var(--ifm-navbar-height);position:-webkit-sticky;position:sticky;top:0;transition:opacity 50ms;width:var(--doc-sidebar-width)}.sidebarWithHideableNavbar_VlPv{padding-top:0}.sidebarHidden_OqfG{height:0;opacity:0;overflow:hidden;visibility:hidden}.sidebarLogo_hmkv{align-items:center;color:inherit!important;display:flex!important;margin:0 var(--ifm-navbar-padding-horizontal);max-height:var(--ifm-navbar-height);min-height:var(--ifm-navbar-height);text-decoration:none!important}.sidebarLogo_hmkv img{height:2rem;margin-right:.5rem}.menu_cyFh{flex-grow:1;padding:.5rem}.menuWithAnnouncementBar_\+O1J{margin-bottom:var(--docusaurus-announcement-bar-height)}.collapseSidebarButton_eoK2{background-color:var(--ifm-button-background-color);border:1px solid var(--ifm-toc-border-color);border-radius:0;bottom:0;display:block!important;height:40px;position:-webkit-sticky;position:sticky}.collapseSidebarButtonIcon_e\+kA{margin-top:4px;transform:rotate(180deg)}.expandSidebarButtonIcon_cxi8,html[dir=rtl] .collapseSidebarButtonIcon_e\+kA{transform:rotate(0)}html[data-theme=dark] .collapseSidebarButton_eoK2,html[data-theme=dark] .collapsedDocSidebar_zZpm:focus,html[data-theme=dark] .collapsedDocSidebar_zZpm:hover{background-color:var(--collapse-button-bg-color-dark)}.collapsedDocSidebar_zZpm:focus,.collapsedDocSidebar_zZpm:hover,html[data-theme=dark] .collapseSidebarButton_eoK2:focus,html[data-theme=dark] .collapseSidebarButton_eoK2:hover{background-color:var(--ifm-color-emphasis-200)}.lastUpdated_mt2f{text-align:right}.docMainContainer_r8cw{flex-grow:1;max-width:calc(100% - var(--doc-sidebar-width))}.docMainContainerEnhanced_SOUu{max-width:calc(100% - var(--doc-sidebar-hidden-width))}.docSidebarContainer_0YBq{border-right:1px solid var(--ifm-toc-border-color);-webkit-clip-path:inset(0);clip-path:inset(0);display:block;margin-top:calc(var(--ifm-navbar-height)*-1);transition:width var(--ifm-transition-fast) ease;width:var(--doc-sidebar-width);will-change:width}.docSidebarContainerHidden_Qlt2{cursor:pointer;width:var(--doc-sidebar-hidden-width)}.collapsedDocSidebar_zZpm{align-items:center;display:flex;height:100%;justify-content:center;max-height:100vh;position:-webkit-sticky;position:sticky;top:0;transition:background-color var(--ifm-transition-fast) ease}html[dir=rtl] .expandSidebarButtonIcon_cxi8{transform:rotate(180deg)}.docItemWrapperEnhanced_aT5H{max-width:calc(var(--ifm-container-width) + var(--doc-sidebar-width))!important}.docItemCol_zHA2{max-width:75%!important}.tocMobile_Tx6Y{display:none}}@media (min-width:1440px){.container{max-width:var(--ifm-container-width-xl)}}@media (max-width:996px){.row .col.col.col{--ifm-col-width:100%;flex-basis:var(--ifm-col-width);margin-left:0}.footer{--ifm-footer-padding-horizontal:0}.footer__link-separator,.navbar__item,.sidebar_q\+wC,.tableOfContents_vrFS,.toggle_2i4l{display:none}.footer__col{margin-bottom:calc(var(--ifm-spacing-vertical)*3)}.footer__link-item{display:block}.hero{padding-left:0;padding-right:0}.navbar>.container,.navbar>.container-fluid{padding:0}.navbar__toggle{display:inherit}.navbar__search-input{width:9rem}.pills--block,.tabs--block{flex-direction:column}.pills--block .pills__item:not(:first-child){margin-top:var(--ifm-pills-spacing)}.pills--block .pills__item:not(:last-child){margin-bottom:var(--ifm-pills-spacing)}.tabs--block .tabs__item:not(:first-child){margin-top:var(--ifm-tabs-spacing)}.tabs--block .tabs__item:not(:last-child){margin-bottom:var(--ifm-tabs-spacing)}.searchBox_Utm0{position:absolute;right:var(--ifm-navbar-padding-horizontal)}.docItemContainer_VKW9{padding:0 .3rem}}@media only screen and (max-width:996px){.searchQueryColumn_qeTZ,.searchResultsColumn_V1kT{max-width:60%!important}.searchLogoColumn_8GYL,.searchVersionColumn_2Kfj{max-width:40%!important}.searchLogoColumn_8GYL{padding-left:0!important}}@media screen and (max-width:966px){.heroBanner_etFc{padding:2rem}}@media (max-width:750px){.DocSearch-Button-Keys,.DocSearch-Button-Placeholder,.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%;max-height:calc(var(--docsearch-vh,1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh,1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh,1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Cancel{-webkit-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:0;overflow:hidden;padding:0;-webkit-user-select:none;-ms-user-select:none;user-select:none;white-space:nowrap}}@media screen and (max-width:576px){.searchQueryColumn_qeTZ{max-width:100%!important}.searchVersionColumn_2Kfj{max-width:100%!important;padding-left:var(--ifm-spacing-horizontal)!important}}@media (hover:hover){.backToTopButton_i9tI:hover{background-color:var(--ifm-color-emphasis-300)}}@media (pointer:fine){.thin-scrollbar{scrollbar-width:thin}.thin-scrollbar::-webkit-scrollbar{height:var(--ifm-scrollbar-size);width:var(--ifm-scrollbar-size)}.thin-scrollbar::-webkit-scrollbar-track{background:var(--ifm-scrollbar-track-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb{background:var(--ifm-scrollbar-thumb-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--ifm-scrollbar-thumb-hover-background-color)}}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{stroke-width:var(--docsearch-icon-stroke-width);animation:none;-webkit-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0}.DocSearch-Hit--deleting,.DocSearch-Hit--favoriting{transition:none}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}@media print{.announcementBar_axC9,.footer,.menu,.navbar,.pagination-nav,.table-of-contents{display:none}.tabs{page-break-inside:avoid}.codeBlockLines_1zSZ{white-space:pre-wrap}} \ No newline at end of file diff --git a/assets/css/styles.4d847056.css b/assets/css/styles.4d847056.css new file mode 100644 index 0000000..7c7c26b --- /dev/null +++ b/assets/css/styles.4d847056.css @@ -0,0 +1 @@ +.col,.container{padding:0 var(--ifm-spacing-horizontal);width:100%}.markdown>h2,.markdown>h3,.markdown>h4,.markdown>h5,.markdown>h6{margin-bottom:calc(var(--ifm-heading-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown li,body{word-wrap:break-word}body,ol ol,ol ul,ul ol,ul ul{margin:0}pre,table{overflow:auto}blockquote,pre{margin:0 0 var(--ifm-spacing-vertical)}.breadcrumbs__link,.button{transition-timing-function:var(--ifm-transition-timing-default)}.button,code{vertical-align:middle}.button--outline.button--active,.button--outline:active,.button--outline:hover,:root{--ifm-button-color:var(--ifm-font-color-base-inverse)}.menu__link:hover,a{transition:color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.navbar--dark,:root{--ifm-navbar-link-hover-color:var(--ifm-color-primary)}.menu,.navbar-sidebar{overflow-x:hidden}:root,html[data-theme=dark]{--ifm-color-emphasis-500:var(--ifm-color-gray-500)}.toggleButton_gllP,html{-webkit-tap-highlight-color:transparent}*,.DocSearch-Container,.DocSearch-Container *{box-sizing:border-box}:root{--ifm-color-scheme:light;--ifm-dark-value:10%;--ifm-darker-value:15%;--ifm-darkest-value:30%;--ifm-light-value:15%;--ifm-lighter-value:30%;--ifm-lightest-value:50%;--ifm-contrast-background-value:90%;--ifm-contrast-foreground-value:70%;--ifm-contrast-background-dark-value:70%;--ifm-contrast-foreground-dark-value:90%;--ifm-color-primary:#3578e5;--ifm-color-secondary:#ebedf0;--ifm-color-success:#00a400;--ifm-color-info:#54c7ec;--ifm-color-warning:#ffba00;--ifm-color-danger:#fa383e;--ifm-color-primary-dark:#306cce;--ifm-color-primary-darker:#2d66c3;--ifm-color-primary-darkest:#2554a0;--ifm-color-primary-light:#538ce9;--ifm-color-primary-lighter:#72a1ed;--ifm-color-primary-lightest:#9abcf2;--ifm-color-primary-contrast-background:#ebf2fc;--ifm-color-primary-contrast-foreground:#102445;--ifm-color-secondary-dark:#d4d5d8;--ifm-color-secondary-darker:#c8c9cc;--ifm-color-secondary-darkest:#a4a6a8;--ifm-color-secondary-light:#eef0f2;--ifm-color-secondary-lighter:#f1f2f5;--ifm-color-secondary-lightest:#f5f6f8;--ifm-color-secondary-contrast-background:#fdfdfe;--ifm-color-secondary-contrast-foreground:#474748;--ifm-color-success-dark:#009400;--ifm-color-success-darker:#008b00;--ifm-color-success-darkest:#007300;--ifm-color-success-light:#26b226;--ifm-color-success-lighter:#4dbf4d;--ifm-color-success-lightest:#80d280;--ifm-color-success-contrast-background:#e6f6e6;--ifm-color-success-contrast-foreground:#003100;--ifm-color-info-dark:#4cb3d4;--ifm-color-info-darker:#47a9c9;--ifm-color-info-darkest:#3b8ba5;--ifm-color-info-light:#6ecfef;--ifm-color-info-lighter:#87d8f2;--ifm-color-info-lightest:#aae3f6;--ifm-color-info-contrast-background:#eef9fd;--ifm-color-info-contrast-foreground:#193c47;--ifm-color-warning-dark:#e6a700;--ifm-color-warning-darker:#d99e00;--ifm-color-warning-darkest:#b38200;--ifm-color-warning-light:#ffc426;--ifm-color-warning-lighter:#ffcf4d;--ifm-color-warning-lightest:#ffdd80;--ifm-color-warning-contrast-background:#fff8e6;--ifm-color-warning-contrast-foreground:#4d3800;--ifm-color-danger-dark:#e13238;--ifm-color-danger-darker:#d53035;--ifm-color-danger-darkest:#af272b;--ifm-color-danger-light:#fb565b;--ifm-color-danger-lighter:#fb7478;--ifm-color-danger-lightest:#fd9c9f;--ifm-color-danger-contrast-background:#ffebec;--ifm-color-danger-contrast-foreground:#4b1113;--ifm-color-white:#fff;--ifm-color-black:#000;--ifm-color-gray-0:var(--ifm-color-white);--ifm-color-gray-100:#f5f6f7;--ifm-color-gray-200:#ebedf0;--ifm-color-gray-300:#dadde1;--ifm-color-gray-400:#ccd0d5;--ifm-color-gray-500:#bec3c9;--ifm-color-gray-600:#8d949e;--ifm-color-gray-700:#606770;--ifm-color-gray-800:#444950;--ifm-color-gray-900:#1c1e21;--ifm-color-gray-1000:var(--ifm-color-black);--ifm-color-emphasis-0:var(--ifm-color-gray-0);--ifm-color-emphasis-100:var(--ifm-color-gray-100);--ifm-color-emphasis-200:var(--ifm-color-gray-200);--ifm-color-emphasis-300:var(--ifm-color-gray-300);--ifm-color-emphasis-400:var(--ifm-color-gray-400);--ifm-color-emphasis-600:var(--ifm-color-gray-600);--ifm-color-emphasis-700:var(--ifm-color-gray-700);--ifm-color-emphasis-800:var(--ifm-color-gray-800);--ifm-color-emphasis-900:var(--ifm-color-gray-900);--ifm-color-emphasis-1000:var(--ifm-color-gray-1000);--ifm-color-content:var(--ifm-color-emphasis-900);--ifm-color-content-inverse:var(--ifm-color-emphasis-0);--ifm-color-content-secondary:#525860;--ifm-background-color:transparent;--ifm-background-surface-color:var(--ifm-color-content-inverse);--ifm-global-border-width:1px;--ifm-global-radius:0.4rem;--ifm-hover-overlay:rgba(0,0,0,.05);--ifm-font-color-base:var(--ifm-color-content);--ifm-font-color-base-inverse:var(--ifm-color-content-inverse);--ifm-font-color-secondary:var(--ifm-color-content-secondary);--ifm-font-family-base:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--ifm-font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--ifm-font-size-base:100%;--ifm-font-weight-light:300;--ifm-font-weight-normal:400;--ifm-font-weight-semibold:500;--ifm-font-weight-bold:700;--ifm-font-weight-base:var(--ifm-font-weight-normal);--ifm-line-height-base:1.65;--ifm-global-spacing:1rem;--ifm-spacing-vertical:var(--ifm-global-spacing);--ifm-spacing-horizontal:var(--ifm-global-spacing);--ifm-transition-fast:200ms;--ifm-transition-slow:400ms;--ifm-transition-timing-default:cubic-bezier(0.08,0.52,0.52,1);--ifm-global-shadow-lw:0 1px 2px 0 rgba(0,0,0,.1);--ifm-global-shadow-md:0 5px 40px rgba(0,0,0,.2);--ifm-global-shadow-tl:0 12px 28px 0 rgba(0,0,0,.2),0 2px 4px 0 rgba(0,0,0,.1);--ifm-z-index-dropdown:100;--ifm-z-index-fixed:200;--ifm-z-index-overlay:400;--ifm-container-width:1140px;--ifm-container-width-xl:1320px;--ifm-code-background:#f6f7f8;--ifm-code-border-radius:var(--ifm-global-radius);--ifm-code-font-size:90%;--ifm-code-padding-horizontal:0.1rem;--ifm-code-padding-vertical:0.1rem;--ifm-pre-background:var(--ifm-code-background);--ifm-pre-border-radius:var(--ifm-code-border-radius);--ifm-pre-color:inherit;--ifm-pre-line-height:1.45;--ifm-pre-padding:1rem;--ifm-heading-color:inherit;--ifm-heading-margin-top:0;--ifm-heading-margin-bottom:var(--ifm-spacing-vertical);--ifm-heading-font-family:var(--ifm-font-family-base);--ifm-heading-font-weight:var(--ifm-font-weight-bold);--ifm-heading-line-height:1.25;--ifm-h1-font-size:2rem;--ifm-h2-font-size:1.5rem;--ifm-h3-font-size:1.25rem;--ifm-h4-font-size:1rem;--ifm-h5-font-size:0.875rem;--ifm-h6-font-size:0.85rem;--ifm-image-alignment-padding:1.25rem;--ifm-leading-desktop:1.25;--ifm-leading:calc(var(--ifm-leading-desktop)*1rem);--ifm-list-left-padding:2rem;--ifm-list-margin:1rem;--ifm-list-item-margin:0.25rem;--ifm-list-paragraph-margin:1rem;--ifm-table-cell-padding:0.75rem;--ifm-table-background:transparent;--ifm-table-stripe-background:rgba(0,0,0,.03);--ifm-table-border-width:1px;--ifm-table-border-color:var(--ifm-color-emphasis-300);--ifm-table-head-background:inherit;--ifm-table-head-color:inherit;--ifm-table-head-font-weight:var(--ifm-font-weight-bold);--ifm-table-cell-color:inherit;--ifm-link-color:var(--ifm-color-primary);--ifm-link-decoration:none;--ifm-link-hover-color:var(--ifm-link-color);--ifm-link-hover-decoration:underline;--ifm-paragraph-margin-bottom:var(--ifm-leading);--ifm-blockquote-font-size:var(--ifm-font-size-base);--ifm-blockquote-border-left-width:2px;--ifm-blockquote-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-blockquote-padding-vertical:0;--ifm-blockquote-shadow:none;--ifm-blockquote-color:var(--ifm-color-emphasis-800);--ifm-blockquote-border-color:var(--ifm-color-emphasis-300);--ifm-hr-background-color:var(--ifm-color-emphasis-500);--ifm-hr-height:1px;--ifm-hr-margin-vertical:1.5rem;--ifm-scrollbar-size:7px;--ifm-scrollbar-track-background-color:#f1f1f1;--ifm-scrollbar-thumb-background-color:silver;--ifm-scrollbar-thumb-hover-background-color:#a7a7a7;--ifm-alert-background-color:inherit;--ifm-alert-border-color:inherit;--ifm-alert-border-radius:var(--ifm-global-radius);--ifm-alert-border-width:0px;--ifm-alert-border-left-width:5px;--ifm-alert-color:var(--ifm-font-color-base);--ifm-alert-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-alert-padding-vertical:var(--ifm-spacing-vertical);--ifm-alert-shadow:var(--ifm-global-shadow-lw);--ifm-avatar-intro-margin:1rem;--ifm-avatar-intro-alignment:inherit;--ifm-avatar-photo-size:3rem;--ifm-badge-background-color:inherit;--ifm-badge-border-color:inherit;--ifm-badge-border-radius:var(--ifm-global-radius);--ifm-badge-border-width:var(--ifm-global-border-width);--ifm-badge-color:var(--ifm-color-white);--ifm-badge-padding-horizontal:calc(var(--ifm-spacing-horizontal)*0.5);--ifm-badge-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-breadcrumb-border-radius:1.5rem;--ifm-breadcrumb-spacing:0.5rem;--ifm-breadcrumb-color-active:var(--ifm-color-primary);--ifm-breadcrumb-item-background-active:var(--ifm-hover-overlay);--ifm-breadcrumb-padding-horizontal:0.8rem;--ifm-breadcrumb-padding-vertical:0.4rem;--ifm-breadcrumb-size-multiplier:1;--ifm-breadcrumb-separator:url('data:image/svg+xml;utf8,');--ifm-breadcrumb-separator-filter:none;--ifm-breadcrumb-separator-size:0.5rem;--ifm-breadcrumb-separator-size-multiplier:1.25;--ifm-button-background-color:inherit;--ifm-button-border-color:var(--ifm-button-background-color);--ifm-button-border-width:var(--ifm-global-border-width);--ifm-button-font-weight:var(--ifm-font-weight-bold);--ifm-button-padding-horizontal:1.5rem;--ifm-button-padding-vertical:0.375rem;--ifm-button-size-multiplier:1;--ifm-button-transition-duration:var(--ifm-transition-fast);--ifm-button-border-radius:calc(var(--ifm-global-radius)*var(--ifm-button-size-multiplier));--ifm-button-group-spacing:2px;--ifm-card-background-color:var(--ifm-background-surface-color);--ifm-card-border-radius:calc(var(--ifm-global-radius)*2);--ifm-card-horizontal-spacing:var(--ifm-global-spacing);--ifm-card-vertical-spacing:var(--ifm-global-spacing);--ifm-toc-border-color:var(--ifm-color-emphasis-300);--ifm-toc-link-color:var(--ifm-color-content-secondary);--ifm-toc-padding-vertical:0.5rem;--ifm-toc-padding-horizontal:0.5rem;--ifm-dropdown-background-color:var(--ifm-background-surface-color);--ifm-dropdown-font-weight:var(--ifm-font-weight-semibold);--ifm-dropdown-link-color:var(--ifm-font-color-base);--ifm-dropdown-hover-background-color:var(--ifm-hover-overlay);--ifm-footer-background-color:var(--ifm-color-emphasis-100);--ifm-footer-color:inherit;--ifm-footer-link-color:var(--ifm-color-emphasis-700);--ifm-footer-link-hover-color:var(--ifm-color-primary);--ifm-footer-link-horizontal-spacing:0.5rem;--ifm-footer-padding-horizontal:calc(var(--ifm-spacing-horizontal)*2);--ifm-footer-padding-vertical:calc(var(--ifm-spacing-vertical)*2);--ifm-footer-title-color:inherit;--ifm-footer-logo-max-width:min(30rem,90vw);--ifm-hero-background-color:var(--ifm-background-surface-color);--ifm-hero-text-color:var(--ifm-color-emphasis-800);--ifm-menu-color:var(--ifm-color-emphasis-700);--ifm-menu-color-active:var(--ifm-color-primary);--ifm-menu-color-background-active:var(--ifm-hover-overlay);--ifm-menu-color-background-hover:var(--ifm-hover-overlay);--ifm-menu-link-padding-horizontal:0.75rem;--ifm-menu-link-padding-vertical:0.375rem;--ifm-menu-link-sublist-icon:url('data:image/svg+xml;utf8,');--ifm-menu-link-sublist-icon-filter:none;--ifm-navbar-background-color:var(--ifm-background-surface-color);--ifm-navbar-height:3.75rem;--ifm-navbar-item-padding-horizontal:0.75rem;--ifm-navbar-item-padding-vertical:0.25rem;--ifm-navbar-link-color:var(--ifm-font-color-base);--ifm-navbar-link-active-color:var(--ifm-link-color);--ifm-navbar-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-navbar-padding-vertical:calc(var(--ifm-spacing-vertical)*0.5);--ifm-navbar-shadow:var(--ifm-global-shadow-lw);--ifm-navbar-search-input-background-color:var(--ifm-color-emphasis-200);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-800);--ifm-navbar-search-input-placeholder-color:var(--ifm-color-emphasis-500);--ifm-navbar-search-input-icon:url('data:image/svg+xml;utf8,');--ifm-navbar-sidebar-width:83vw;--ifm-pagination-border-radius:var(--ifm-global-radius);--ifm-pagination-color-active:var(--ifm-color-primary);--ifm-pagination-font-size:1rem;--ifm-pagination-item-active-background:var(--ifm-hover-overlay);--ifm-pagination-page-spacing:0.2em;--ifm-pagination-padding-horizontal:calc(var(--ifm-spacing-horizontal)*1);--ifm-pagination-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-pagination-nav-border-radius:var(--ifm-global-radius);--ifm-pagination-nav-color-hover:var(--ifm-color-primary);--ifm-pills-color-active:var(--ifm-color-primary);--ifm-pills-color-background-active:var(--ifm-hover-overlay);--ifm-pills-spacing:0.125rem;--ifm-tabs-color:var(--ifm-font-color-secondary);--ifm-tabs-color-active:var(--ifm-color-primary);--ifm-tabs-color-active-border:var(--ifm-tabs-color-active);--ifm-tabs-padding-horizontal:1rem;--ifm-tabs-padding-vertical:1rem;--docusaurus-progress-bar-color:var(--ifm-color-primary);--ifm-color-primary:#1b558c;--ifm-color-primary-dark:#103252;--ifm-color-primary-darker:#0d2c48;--ifm-color-primary-darkest:#0a2135;--ifm-color-primary-light:#2984d8;--ifm-color-primary-lighter:#388ad7;--ifm-color-primary-lightest:#59a6ee;--ifm-code-font-size:95%;--docusaurus-announcement-bar-height:auto;--docusaurus-collapse-button-bg:transparent;--docusaurus-collapse-button-bg-hover:rgba(0,0,0,.1);--doc-sidebar-width:300px;--doc-sidebar-hidden-width:30px;--docsearch-primary-color:#5468ff;--docsearch-text-color:#1c1e21;--docsearch-spacing:12px;--docsearch-icon-stroke-width:1.4;--docsearch-highlight-color:var(--docsearch-primary-color);--docsearch-muted-color:#969faf;--docsearch-container-background:rgba(101,108,133,.8);--docsearch-logo-color:#5468ff;--docsearch-modal-width:560px;--docsearch-modal-height:600px;--docsearch-modal-background:#f5f6f7;--docsearch-modal-shadow:inset 1px 1px 0 0 hsla(0,0%,100%,.5),0 3px 8px 0 #555a64;--docsearch-searchbox-height:56px;--docsearch-searchbox-background:#ebedf0;--docsearch-searchbox-focus-background:#fff;--docsearch-searchbox-shadow:inset 0 0 0 2px var(--docsearch-primary-color);--docsearch-hit-height:56px;--docsearch-hit-color:#444950;--docsearch-hit-active-color:#fff;--docsearch-hit-background:#fff;--docsearch-hit-shadow:0 1px 3px 0 #d4d9e1;--docsearch-key-gradient:linear-gradient(-225deg,#d5dbe4,#f8f8f8);--docsearch-key-shadow:inset 0 -2px 0 0 #cdcde6,inset 0 0 1px 1px #fff,0 1px 2px 1px rgba(30,35,90,.4);--docsearch-footer-height:44px;--docsearch-footer-background:#fff;--docsearch-footer-shadow:0 -1px 0 0 #e0e3e8,0 -3px 6px 0 rgba(69,98,155,.12);--docsearch-primary-color:var(--ifm-color-primary);--docsearch-text-color:var(--ifm-font-color-base);--docusaurus-tag-list-border:var(--ifm-color-emphasis-300)}.badge--danger,.badge--info,.badge--primary,.badge--secondary,.badge--success,.badge--warning{--ifm-badge-border-color:var(--ifm-badge-background-color)}.button--link,.button--outline{--ifm-button-background-color:transparent}html{-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--ifm-background-color);color:var(--ifm-font-color-base);color-scheme:var(--ifm-color-scheme);font:var(--ifm-font-size-base)/var(--ifm-line-height-base) var(--ifm-font-family-base);text-rendering:optimizelegibility}iframe{border:0;color-scheme:auto}.container{margin:0 auto;max-width:var(--ifm-container-width)}.container--fluid{max-width:inherit}.row{display:flex;flex-wrap:wrap;margin:0 calc(var(--ifm-spacing-horizontal)*-1)}.margin-bottom--none,.margin-vert--none,.markdown>:last-child{margin-bottom:0!important}.margin-top--none,.margin-vert--none{margin-top:0!important}.row--no-gutters{margin-left:0;margin-right:0}.margin-horiz--none,.margin-right--none{margin-right:0!important}.row--no-gutters>.col{padding-left:0;padding-right:0}.row--align-top{align-items:flex-start}.row--align-bottom{align-items:flex-end}.menuExternalLink_NmtK,.row--align-center{align-items:center}.row--align-stretch{align-items:stretch}.row--align-baseline{align-items:baseline}.col{--ifm-col-width:100%;flex:1 0;margin-left:0;max-width:var(--ifm-col-width)}.padding-bottom--none,.padding-vert--none{padding-bottom:0!important}.padding-top--none,.padding-vert--none{padding-top:0!important}.padding-horiz--none,.padding-left--none{padding-left:0!important}.padding-horiz--none,.padding-right--none{padding-right:0!important}.col[class*=col--]{flex:0 0 var(--ifm-col-width)}.col--1{--ifm-col-width:8.33333%}.col--offset-1{margin-left:8.33333%}.col--2{--ifm-col-width:16.66667%}.col--offset-2{margin-left:16.66667%}.col--3{--ifm-col-width:25%}.col--offset-3{margin-left:25%}.col--4{--ifm-col-width:33.33333%}.col--offset-4{margin-left:33.33333%}.col--5{--ifm-col-width:41.66667%}.col--offset-5{margin-left:41.66667%}.col--6{--ifm-col-width:50%}.col--offset-6{margin-left:50%}.col--7{--ifm-col-width:58.33333%}.col--offset-7{margin-left:58.33333%}.col--8{--ifm-col-width:66.66667%}.col--offset-8{margin-left:66.66667%}.col--9{--ifm-col-width:75%}.col--offset-9{margin-left:75%}.col--10{--ifm-col-width:83.33333%}.col--offset-10{margin-left:83.33333%}.col--11{--ifm-col-width:91.66667%}.col--offset-11{margin-left:91.66667%}.col--12{--ifm-col-width:100%}.col--offset-12{margin-left:100%}.margin-horiz--none,.margin-left--none{margin-left:0!important}.margin--none{margin:0!important}.margin-bottom--xs,.margin-vert--xs{margin-bottom:.25rem!important}.margin-top--xs,.margin-vert--xs{margin-top:.25rem!important}.margin-horiz--xs,.margin-left--xs{margin-left:.25rem!important}.margin-horiz--xs,.margin-right--xs{margin-right:.25rem!important}.margin--xs{margin:.25rem!important}.margin-bottom--sm,.margin-vert--sm{margin-bottom:.5rem!important}.margin-top--sm,.margin-vert--sm{margin-top:.5rem!important}.margin-horiz--sm,.margin-left--sm{margin-left:.5rem!important}.margin-horiz--sm,.margin-right--sm{margin-right:.5rem!important}.margin--sm{margin:.5rem!important}.margin-bottom--md,.margin-vert--md{margin-bottom:1rem!important}.margin-top--md,.margin-vert--md{margin-top:1rem!important}.margin-horiz--md,.margin-left--md{margin-left:1rem!important}.margin-horiz--md,.margin-right--md{margin-right:1rem!important}.margin--md{margin:1rem!important}.margin-bottom--lg,.margin-vert--lg{margin-bottom:2rem!important}.margin-top--lg,.margin-vert--lg{margin-top:2rem!important}.margin-horiz--lg,.margin-left--lg{margin-left:2rem!important}.margin-horiz--lg,.margin-right--lg{margin-right:2rem!important}.margin--lg{margin:2rem!important}.margin-bottom--xl,.margin-vert--xl{margin-bottom:5rem!important}.margin-top--xl,.margin-vert--xl{margin-top:5rem!important}.margin-horiz--xl,.margin-left--xl{margin-left:5rem!important}.margin-horiz--xl,.margin-right--xl{margin-right:5rem!important}.margin--xl{margin:5rem!important}.padding--none{padding:0!important}.padding-bottom--xs,.padding-vert--xs{padding-bottom:.25rem!important}.padding-top--xs,.padding-vert--xs{padding-top:.25rem!important}.padding-horiz--xs,.padding-left--xs{padding-left:.25rem!important}.padding-horiz--xs,.padding-right--xs{padding-right:.25rem!important}.padding--xs{padding:.25rem!important}.padding-bottom--sm,.padding-vert--sm{padding-bottom:.5rem!important}.padding-top--sm,.padding-vert--sm{padding-top:.5rem!important}.padding-horiz--sm,.padding-left--sm{padding-left:.5rem!important}.padding-horiz--sm,.padding-right--sm{padding-right:.5rem!important}.padding--sm{padding:.5rem!important}.padding-bottom--md,.padding-vert--md{padding-bottom:1rem!important}.padding-top--md,.padding-vert--md{padding-top:1rem!important}.padding-horiz--md,.padding-left--md{padding-left:1rem!important}.padding-horiz--md,.padding-right--md{padding-right:1rem!important}.padding--md{padding:1rem!important}.padding-bottom--lg,.padding-vert--lg{padding-bottom:2rem!important}.padding-top--lg,.padding-vert--lg{padding-top:2rem!important}.padding-horiz--lg,.padding-left--lg{padding-left:2rem!important}.padding-horiz--lg,.padding-right--lg{padding-right:2rem!important}.padding--lg{padding:2rem!important}.padding-bottom--xl,.padding-vert--xl{padding-bottom:5rem!important}.padding-top--xl,.padding-vert--xl{padding-top:5rem!important}.padding-horiz--xl,.padding-left--xl{padding-left:5rem!important}.padding-horiz--xl,.padding-right--xl{padding-right:5rem!important}.padding--xl{padding:5rem!important}code{background-color:var(--ifm-code-background);border:.1rem solid rgba(0,0,0,.1);border-radius:var(--ifm-code-border-radius);font-family:var(--ifm-font-family-monospace);font-size:var(--ifm-code-font-size);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal)}a code{color:inherit}pre{background-color:var(--ifm-pre-background);border-radius:var(--ifm-pre-border-radius);color:var(--ifm-pre-color);font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace);padding:var(--ifm-pre-padding)}pre code{background-color:initial;border:none;font-size:100%;line-height:inherit;padding:0}kbd{background-color:var(--ifm-color-emphasis-0);border:1px solid var(--ifm-color-emphasis-400);border-radius:.2rem;box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-400);color:var(--ifm-color-emphasis-800);font:80% var(--ifm-font-family-monospace);padding:.15rem .3rem}h1,h2,h3,h4,h5,h6{color:var(--ifm-heading-color);font-family:var(--ifm-heading-font-family);font-weight:var(--ifm-heading-font-weight);line-height:var(--ifm-heading-line-height);margin:var(--ifm-heading-margin-top) 0 var(--ifm-heading-margin-bottom) 0}h1{font-size:var(--ifm-h1-font-size)}h2{font-size:var(--ifm-h2-font-size)}h3{font-size:var(--ifm-h3-font-size)}h4{font-size:var(--ifm-h4-font-size)}h5{font-size:var(--ifm-h5-font-size)}h6{font-size:var(--ifm-h6-font-size)}img{max-width:100%}img[align=right]{padding-left:var(--image-alignment-padding)}img[align=left]{padding-right:var(--image-alignment-padding)}.markdown{--ifm-h1-vertical-rhythm-top:3;--ifm-h2-vertical-rhythm-top:2;--ifm-h3-vertical-rhythm-top:1.5;--ifm-heading-vertical-rhythm-top:1.25;--ifm-h1-vertical-rhythm-bottom:1.25;--ifm-heading-vertical-rhythm-bottom:1}.markdown:after,.markdown:before{content:"";display:table}.markdown:after{clear:both}.markdown h1:first-child{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-h1-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown>h2{--ifm-h2-font-size:2rem;margin-top:calc(var(--ifm-h2-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h3{--ifm-h3-font-size:1.5rem;margin-top:calc(var(--ifm-h3-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h4,.markdown>h5,.markdown>h6{margin-top:calc(var(--ifm-heading-vertical-rhythm-top)*var(--ifm-leading))}.markdown>p,.markdown>pre,.markdown>ul{margin-bottom:var(--ifm-leading)}.markdown li>p{margin-top:var(--ifm-list-paragraph-margin)}.markdown li+li{margin-top:var(--ifm-list-item-margin)}ol,ul{margin:0 0 var(--ifm-list-margin);padding-left:var(--ifm-list-left-padding)}ol ol,ul ol{list-style-type:lower-roman}ol ol ol,ol ul ol,ul ol ol,ul ul ol{list-style-type:lower-alpha}table{border-collapse:collapse;display:block;margin-bottom:var(--ifm-spacing-vertical)}table thead tr{border-bottom:2px solid var(--ifm-table-border-color)}table thead,table tr:nth-child(2n){background-color:var(--ifm-table-stripe-background)}table tr{background-color:var(--ifm-table-background);border-top:var(--ifm-table-border-width) solid var(--ifm-table-border-color)}table td,table th{border:var(--ifm-table-border-width) solid var(--ifm-table-border-color);padding:var(--ifm-table-cell-padding)}table th{background-color:var(--ifm-table-head-background);color:var(--ifm-table-head-color);font-weight:var(--ifm-table-head-font-weight)}table td{color:var(--ifm-table-cell-color)}strong{font-weight:var(--ifm-font-weight-bold)}a{color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}a:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button:hover,.text--no-decoration,.text--no-decoration:hover,a:not([href]){text-decoration:none}p{margin:0 0 var(--ifm-paragraph-margin-bottom)}blockquote{border-left:var(--ifm-blockquote-border-left-width) solid var(--ifm-blockquote-border-color);box-shadow:var(--ifm-blockquote-shadow);color:var(--ifm-blockquote-color);font-size:var(--ifm-blockquote-font-size);padding:var(--ifm-blockquote-padding-vertical) var(--ifm-blockquote-padding-horizontal)}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{background-color:var(--ifm-hr-background-color);border:0;height:var(--ifm-hr-height);margin:var(--ifm-hr-margin-vertical) 0}.shadow--lw{box-shadow:var(--ifm-global-shadow-lw)!important}.shadow--md{box-shadow:var(--ifm-global-shadow-md)!important}.shadow--tl{box-shadow:var(--ifm-global-shadow-tl)!important}.text--primary,.wordWrapButtonEnabled_EoeP .wordWrapButtonIcon_Bwma{color:var(--ifm-color-primary)}.text--secondary{color:var(--ifm-color-secondary)}.text--success{color:var(--ifm-color-success)}.text--info{color:var(--ifm-color-info)}.text--warning{color:var(--ifm-color-warning)}.text--danger{color:var(--ifm-color-danger)}.text--center{text-align:center}.text--left{text-align:left}.text--justify{text-align:justify}.text--right{text-align:right}.text--capitalize{text-transform:capitalize}.text--lowercase{text-transform:lowercase}.admonitionHeading_tbUL,.alert__heading,.text--uppercase{text-transform:uppercase}.text--light{font-weight:var(--ifm-font-weight-light)}.text--normal{font-weight:var(--ifm-font-weight-normal)}.text--semibold{font-weight:var(--ifm-font-weight-semibold)}.text--bold{font-weight:var(--ifm-font-weight-bold)}.text--italic{font-style:italic}.text--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text--break{word-wrap:break-word!important;word-break:break-word!important}.clean-btn{background:none;border:none;color:inherit;cursor:pointer;font-family:inherit;padding:0}.alert,.alert .close{color:var(--ifm-alert-foreground-color)}.clean-list{list-style:none;padding-left:0}.alert--primary{--ifm-alert-background-color:var(--ifm-color-primary-contrast-background);--ifm-alert-background-color-highlight:rgba(53,120,229,.15);--ifm-alert-foreground-color:var(--ifm-color-primary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-primary-dark)}.alert--secondary{--ifm-alert-background-color:var(--ifm-color-secondary-contrast-background);--ifm-alert-background-color-highlight:rgba(235,237,240,.15);--ifm-alert-foreground-color:var(--ifm-color-secondary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-secondary-dark)}.alert--success{--ifm-alert-background-color:var(--ifm-color-success-contrast-background);--ifm-alert-background-color-highlight:rgba(0,164,0,.15);--ifm-alert-foreground-color:var(--ifm-color-success-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-success-dark)}.alert--info{--ifm-alert-background-color:var(--ifm-color-info-contrast-background);--ifm-alert-background-color-highlight:rgba(84,199,236,.15);--ifm-alert-foreground-color:var(--ifm-color-info-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-info-dark)}.alert--warning{--ifm-alert-background-color:var(--ifm-color-warning-contrast-background);--ifm-alert-background-color-highlight:rgba(255,186,0,.15);--ifm-alert-foreground-color:var(--ifm-color-warning-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-warning-dark)}.alert--danger{--ifm-alert-background-color:var(--ifm-color-danger-contrast-background);--ifm-alert-background-color-highlight:rgba(250,56,62,.15);--ifm-alert-foreground-color:var(--ifm-color-danger-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-danger-dark)}.alert{--ifm-code-background:var(--ifm-alert-background-color-highlight);--ifm-link-color:var(--ifm-alert-foreground-color);--ifm-link-hover-color:var(--ifm-alert-foreground-color);--ifm-link-decoration:underline;--ifm-tabs-color:var(--ifm-alert-foreground-color);--ifm-tabs-color-active:var(--ifm-alert-foreground-color);--ifm-tabs-color-active-border:var(--ifm-alert-border-color);background-color:var(--ifm-alert-background-color);border:var(--ifm-alert-border-width) solid var(--ifm-alert-border-color);border-left-width:var(--ifm-alert-border-left-width);border-radius:var(--ifm-alert-border-radius);box-shadow:var(--ifm-alert-shadow);padding:var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal)}.alert__heading{align-items:center;display:flex;font:700 var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.5rem}.alert__icon{display:inline-flex;margin-right:.4em}.alert__icon svg{fill:var(--ifm-alert-foreground-color);stroke:var(--ifm-alert-foreground-color);stroke-width:0}.alert .close{margin:calc(var(--ifm-alert-padding-vertical)*-1) calc(var(--ifm-alert-padding-horizontal)*-1) 0 0;opacity:.75}.alert .close:focus,.alert .close:hover{opacity:1}.alert a{text-decoration-color:var(--ifm-alert-border-color)}.alert a:hover{text-decoration-thickness:2px}.avatar{column-gap:var(--ifm-avatar-intro-margin);display:flex}.avatar__photo{border-radius:50%;display:block;height:var(--ifm-avatar-photo-size);overflow:hidden;width:var(--ifm-avatar-photo-size)}.card--full-height,.navbar__logo img,body,html{height:100%}.avatar__photo--sm{--ifm-avatar-photo-size:2rem}.avatar__photo--lg{--ifm-avatar-photo-size:4rem}.avatar__photo--xl{--ifm-avatar-photo-size:6rem}.avatar__intro{display:flex;flex:1 1;flex-direction:column;justify-content:center;text-align:var(--ifm-avatar-intro-alignment)}.badge,.breadcrumbs__item,.breadcrumbs__link,.button,.dropdown>.navbar__link:after{display:inline-block}.avatar__name{font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base)}.avatar__subtitle{margin-top:.25rem}.avatar--vertical{--ifm-avatar-intro-alignment:center;--ifm-avatar-intro-margin:0.5rem;align-items:center;flex-direction:column}.badge{background-color:var(--ifm-badge-background-color);border:var(--ifm-badge-border-width) solid var(--ifm-badge-border-color);border-radius:var(--ifm-badge-border-radius);color:var(--ifm-badge-color);font-size:75%;font-weight:var(--ifm-font-weight-bold);line-height:1;padding:var(--ifm-badge-padding-vertical) var(--ifm-badge-padding-horizontal)}.badge--primary{--ifm-badge-background-color:var(--ifm-color-primary)}.badge--secondary{--ifm-badge-background-color:var(--ifm-color-secondary);color:var(--ifm-color-black)}.breadcrumbs__link,.button.button--secondary.button--outline:not(.button--active):not(:hover){color:var(--ifm-font-color-base)}.badge--success{--ifm-badge-background-color:var(--ifm-color-success)}.badge--info{--ifm-badge-background-color:var(--ifm-color-info)}.badge--warning{--ifm-badge-background-color:var(--ifm-color-warning)}.badge--danger{--ifm-badge-background-color:var(--ifm-color-danger)}.breadcrumbs{margin-bottom:0;padding-left:0}.breadcrumbs__item:not(:last-child):after{background:var(--ifm-breadcrumb-separator) center;content:" ";display:inline-block;filter:var(--ifm-breadcrumb-separator-filter);height:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier));margin:0 var(--ifm-breadcrumb-spacing);opacity:.5;width:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier))}.breadcrumbs__item--active .breadcrumbs__link{background:var(--ifm-breadcrumb-item-background-active);color:var(--ifm-breadcrumb-color-active)}.breadcrumbs__link{border-radius:var(--ifm-breadcrumb-border-radius);font-size:calc(1rem*var(--ifm-breadcrumb-size-multiplier));padding:calc(var(--ifm-breadcrumb-padding-vertical)*var(--ifm-breadcrumb-size-multiplier)) calc(var(--ifm-breadcrumb-padding-horizontal)*var(--ifm-breadcrumb-size-multiplier));transition-duration:var(--ifm-transition-fast);transition-property:background,color}.breadcrumbs__link:link:hover,.breadcrumbs__link:visited:hover,area.breadcrumbs__link[href]:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs__link:-webkit-any-link:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs__link:any-link:hover{background:var(--ifm-breadcrumb-item-background-active);text-decoration:none}.breadcrumbs--sm{--ifm-breadcrumb-size-multiplier:0.8}.breadcrumbs--lg{--ifm-breadcrumb-size-multiplier:1.2}.button{background-color:var(--ifm-button-background-color);border:var(--ifm-button-border-width) solid var(--ifm-button-border-color);border-radius:var(--ifm-button-border-radius);cursor:pointer;font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:var(--ifm-button-font-weight);line-height:1.5;padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));text-align:center;transition-duration:var(--ifm-button-transition-duration);transition-property:color,background,border-color;-webkit-user-select:none;user-select:none;white-space:nowrap}.button,.button:hover{color:var(--ifm-button-color)}.button--outline{--ifm-button-color:var(--ifm-button-border-color)}.button--outline:hover{--ifm-button-background-color:var(--ifm-button-border-color)}.button--link{--ifm-button-border-color:transparent;color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}.button--link.button--active,.button--link:active,.button--link:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button.disabled,.button:disabled,.button[disabled]{opacity:.65;pointer-events:none}.button--sm{--ifm-button-size-multiplier:0.8}.button--lg{--ifm-button-size-multiplier:1.35}.button--block{display:block;width:100%}.button.button--secondary{color:var(--ifm-color-gray-900)}:where(.button--primary){--ifm-button-background-color:var(--ifm-color-primary);--ifm-button-border-color:var(--ifm-color-primary)}:where(.button--primary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-primary-dark);--ifm-button-border-color:var(--ifm-color-primary-dark)}.button--primary.button--active,.button--primary:active{--ifm-button-background-color:var(--ifm-color-primary-darker);--ifm-button-border-color:var(--ifm-color-primary-darker)}:where(.button--secondary){--ifm-button-background-color:var(--ifm-color-secondary);--ifm-button-border-color:var(--ifm-color-secondary)}:where(.button--secondary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-secondary-dark);--ifm-button-border-color:var(--ifm-color-secondary-dark)}.button--secondary.button--active,.button--secondary:active{--ifm-button-background-color:var(--ifm-color-secondary-darker);--ifm-button-border-color:var(--ifm-color-secondary-darker)}:where(.button--success){--ifm-button-background-color:var(--ifm-color-success);--ifm-button-border-color:var(--ifm-color-success)}:where(.button--success):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-success-dark);--ifm-button-border-color:var(--ifm-color-success-dark)}.button--success.button--active,.button--success:active{--ifm-button-background-color:var(--ifm-color-success-darker);--ifm-button-border-color:var(--ifm-color-success-darker)}:where(.button--info){--ifm-button-background-color:var(--ifm-color-info);--ifm-button-border-color:var(--ifm-color-info)}:where(.button--info):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-info-dark);--ifm-button-border-color:var(--ifm-color-info-dark)}.button--info.button--active,.button--info:active{--ifm-button-background-color:var(--ifm-color-info-darker);--ifm-button-border-color:var(--ifm-color-info-darker)}:where(.button--warning){--ifm-button-background-color:var(--ifm-color-warning);--ifm-button-border-color:var(--ifm-color-warning)}:where(.button--warning):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-warning-dark);--ifm-button-border-color:var(--ifm-color-warning-dark)}.button--warning.button--active,.button--warning:active{--ifm-button-background-color:var(--ifm-color-warning-darker);--ifm-button-border-color:var(--ifm-color-warning-darker)}:where(.button--danger){--ifm-button-background-color:var(--ifm-color-danger);--ifm-button-border-color:var(--ifm-color-danger)}:where(.button--danger):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-danger-dark);--ifm-button-border-color:var(--ifm-color-danger-dark)}.button--danger.button--active,.button--danger:active{--ifm-button-background-color:var(--ifm-color-danger-darker);--ifm-button-border-color:var(--ifm-color-danger-darker)}.button-group{display:inline-flex;gap:var(--ifm-button-group-spacing)}.button-group>.button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.button-group>.button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.button-group--block{display:flex;justify-content:stretch}.button-group--block>.button{flex-grow:1}.card{background-color:var(--ifm-card-background-color);border-radius:var(--ifm-card-border-radius);box-shadow:var(--ifm-global-shadow-lw);display:flex;flex-direction:column;overflow:hidden}.card__image{padding-top:var(--ifm-card-vertical-spacing)}.card__image:first-child{padding-top:0}.card__body,.card__footer,.card__header{padding:var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing)}.card__body:not(:last-child),.card__footer:not(:last-child),.card__header:not(:last-child){padding-bottom:0}.card__body>:last-child,.card__footer>:last-child,.card__header>:last-child{margin-bottom:0}.card__footer{margin-top:auto}.table-of-contents{font-size:.8rem;margin-bottom:0;padding:var(--ifm-toc-padding-vertical) 0}.table-of-contents,.table-of-contents ul{list-style:none;padding-left:var(--ifm-toc-padding-horizontal)}.table-of-contents li{margin:var(--ifm-toc-padding-vertical) var(--ifm-toc-padding-horizontal)}.table-of-contents__left-border{border-left:1px solid var(--ifm-toc-border-color)}.table-of-contents__link{color:var(--ifm-toc-link-color);display:block}.table-of-contents__link--active,.table-of-contents__link--active code,.table-of-contents__link:hover,.table-of-contents__link:hover code{color:var(--ifm-color-primary);text-decoration:none}.close{color:var(--ifm-color-black);float:right;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);line-height:1;opacity:.5;padding:1rem;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.close:hover{opacity:.7}.close:focus,.theme-code-block-highlighted-line .codeLineNumber_Tfdd:before{opacity:.8}.dropdown{display:inline-flex;font-weight:var(--ifm-dropdown-font-weight);position:relative;vertical-align:top}.dropdown--hoverable:hover .dropdown__menu,.dropdown--show .dropdown__menu{opacity:1;pointer-events:all;transform:translateY(-1px);visibility:visible}#nprogress,.dropdown__menu,.navbar__item.dropdown .navbar__link:not([href]){pointer-events:none}.dropdown--right .dropdown__menu{left:inherit;right:0}.dropdown--nocaret .navbar__link:after{content:none!important}.dropdown__menu{background-color:var(--ifm-dropdown-background-color);border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-md);left:0;list-style:none;max-height:80vh;min-width:10rem;opacity:0;overflow-y:auto;padding:.5rem;position:absolute;top:calc(100% - var(--ifm-navbar-item-padding-vertical) + .3rem);transform:translateY(-.625rem);transition-duration:var(--ifm-transition-fast);transition-property:opacity,transform,visibility;transition-timing-function:var(--ifm-transition-timing-default);visibility:hidden;z-index:var(--ifm-z-index-dropdown)}.menu__caret,.menu__link,.menu__list-item-collapsible{border-radius:.25rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.dropdown__link{border-radius:.25rem;color:var(--ifm-dropdown-link-color);display:block;font-size:.875rem;margin-top:.2rem;padding:.25rem .5rem;white-space:nowrap}.dropdown__link--active,.dropdown__link:hover{background-color:var(--ifm-dropdown-hover-background-color);color:var(--ifm-dropdown-link-color);text-decoration:none}.dropdown__link--active,.dropdown__link--active:hover{--ifm-dropdown-link-color:var(--ifm-link-color)}.dropdown>.navbar__link:after{border-color:currentcolor transparent;border-style:solid;border-width:.4em .4em 0;content:"";margin-left:.3em;position:relative;top:2px;transform:translateY(-50%)}.footer{background-color:var(--ifm-footer-background-color);color:var(--ifm-footer-color);padding:var(--ifm-footer-padding-vertical) var(--ifm-footer-padding-horizontal)}.footer--dark{--ifm-footer-background-color:#303846;--ifm-footer-color:var(--ifm-footer-link-color);--ifm-footer-link-color:var(--ifm-color-secondary);--ifm-footer-title-color:var(--ifm-color-white)}.footer__links{margin-bottom:1rem}.footer__link-item{color:var(--ifm-footer-link-color);line-height:2}.footer__link-item:hover{color:var(--ifm-footer-link-hover-color)}.footer__link-separator{margin:0 var(--ifm-footer-link-horizontal-spacing)}.footer__logo{margin-top:1rem;max-width:var(--ifm-footer-logo-max-width)}.footer__title{color:var(--ifm-footer-title-color);font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base);margin-bottom:var(--ifm-heading-margin-bottom)}.menu,.navbar__link{font-weight:var(--ifm-font-weight-semibold)}.docItemContainer_Djhp article>:first-child,.docItemContainer_Djhp header+*,.footer__item{margin-top:0}.admonitionContent_S0QG>:last-child,.collapsibleContent_i85q>:last-child,.footer__items{margin-bottom:0}.codeBlockStandalone_MEMb,[type=checkbox]{padding:0}.hero{align-items:center;background-color:var(--ifm-hero-background-color);color:var(--ifm-hero-text-color);display:flex;padding:4rem 2rem}.hero--primary{--ifm-hero-background-color:var(--ifm-color-primary);--ifm-hero-text-color:var(--ifm-font-color-base-inverse)}.hero--dark{--ifm-hero-background-color:#303846;--ifm-hero-text-color:var(--ifm-color-white)}.hero__title,.title_f1Hy{font-size:3rem}.hero__subtitle{font-size:1.5rem}.menu__list{list-style:none;margin:0;padding-left:0}.menu__caret,.menu__link{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu__list .menu__list{flex:0 0 100%;margin-top:.25rem;padding-left:var(--ifm-menu-link-padding-horizontal)}.menu__list-item:not(:first-child){margin-top:.25rem}.menu__list-item--collapsed .menu__list{height:0;overflow:hidden}.details_lb9f[data-collapsed=false].isBrowser_bmU9>summary:before,.details_lb9f[open]:not(.isBrowser_bmU9)>summary:before,.menu__list-item--collapsed .menu__caret:before,.menu__list-item--collapsed .menu__link--sublist:after{transform:rotate(90deg)}.menu__list-item-collapsible{display:flex;flex-wrap:wrap;position:relative}.menu__caret:hover,.menu__link:hover,.menu__list-item-collapsible--active,.menu__list-item-collapsible:hover{background:var(--ifm-menu-color-background-hover)}.menu__list-item-collapsible .menu__link--active,.menu__list-item-collapsible .menu__link:hover{background:none!important}.menu__caret,.menu__link{align-items:center;display:flex}.menu__link{color:var(--ifm-menu-color);flex:1;line-height:1.25}.menu__link:hover{color:var(--ifm-menu-color);text-decoration:none}.menu__caret:before,.menu__link--sublist-caret:after{content:"";height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast) linear;width:1.25rem;filter:var(--ifm-menu-link-sublist-icon-filter)}.menu__link--sublist-caret:after{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;margin-left:auto;min-width:1.25rem}.menu__link--active,.menu__link--active:hover{color:var(--ifm-menu-color-active)}.navbar__brand,.navbar__link{color:var(--ifm-navbar-link-color)}.menu__link--active:not(.menu__link--sublist){background-color:var(--ifm-menu-color-background-active)}.menu__caret:before{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem}.navbar--dark,html[data-theme=dark]{--ifm-menu-link-sublist-icon-filter:invert(100%) sepia(94%) saturate(17%) hue-rotate(223deg) brightness(104%) contrast(98%)}.navbar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-navbar-shadow);height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.docsWrapper_BCFX,.navbar,.navbar>.container,.navbar>.container-fluid{display:flex}.navbar--fixed-top{position:sticky;top:0;z-index:var(--ifm-z-index-fixed)}.navbar-sidebar,.navbar-sidebar__backdrop{bottom:0;opacity:0;position:fixed;transition-timing-function:ease-in-out;left:0;top:0;visibility:hidden}.announcementBar_mb4j,.skipToContent_fXgn{z-index:calc(var(--ifm-z-index-fixed) + 1)}.navbar__inner{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%}.navbar__brand{align-items:center;display:flex;margin-right:1rem;min-width:0}.navbar__brand:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.announcementBarContent_xLdY,.navbar__title{flex:1 1 auto}.navbar__toggle{display:none;margin-right:.5rem}.navbar__logo{flex:0 0 auto;height:2rem;margin-right:.5rem}.navbar__items{align-items:center;display:flex;flex:1;min-width:0}.navbar__items--center{flex:0 0 auto}.navbar__items--center .navbar__brand{margin:0}.navbar__items--center+.navbar__items--right{flex:1}.navbar__items--right{flex:0 0 auto;justify-content:flex-end}.navbar__items--right>:last-child{padding-right:0}.navbar__item{display:inline-block;padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.navbar__link--active,.navbar__link:hover{color:var(--ifm-navbar-link-hover-color);text-decoration:none}.navbar--dark,.navbar--primary{--ifm-menu-color:var(--ifm-color-gray-300);--ifm-navbar-link-color:var(--ifm-color-gray-100);--ifm-navbar-search-input-background-color:hsla(0,0%,100%,.1);--ifm-navbar-search-input-placeholder-color:hsla(0,0%,100%,.5);color:var(--ifm-color-white)}.navbar--dark{--ifm-navbar-background-color:#242526;--ifm-menu-color-background-active:hsla(0,0%,100%,.05);--ifm-navbar-search-input-color:var(--ifm-color-white)}.navbar--primary{--ifm-navbar-background-color:var(--ifm-color-primary);--ifm-navbar-link-hover-color:var(--ifm-color-white);--ifm-menu-color-active:var(--ifm-color-white);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-500)}.navbar__search-input{-webkit-appearance:none;appearance:none;background:var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat .75rem center/1rem 1rem;border:none;border-radius:2rem;color:var(--ifm-navbar-search-input-color);cursor:text;display:inline-block;font-size:.9rem;height:2rem;padding:0 .5rem 0 2.25rem;width:12.5rem}.navbar__search-input::placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar-sidebar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-global-shadow-md);transform:translate3d(-100%,0,0);transition-duration:.25s;transition-property:opacity,visibility,transform;width:var(--ifm-navbar-sidebar-width)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar__items{transform:translateZ(0)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.navbar-sidebar__backdrop{background-color:rgba(0,0,0,.6);right:0;transition-duration:.1s;transition-property:opacity,visibility}.navbar-sidebar__brand{align-items:center;box-shadow:var(--ifm-navbar-shadow);display:flex;flex:1;height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar-sidebar__items{display:flex;height:calc(100% - var(--ifm-navbar-height));transition:transform var(--ifm-transition-fast) ease-in-out}.navbar-sidebar__items--show-secondary{transform:translate3d(calc((var(--ifm-navbar-sidebar-width))*-1),0,0)}.navbar-sidebar__item{flex-shrink:0;padding:.5rem;width:calc(var(--ifm-navbar-sidebar-width))}.navbar-sidebar__back{background:var(--ifm-menu-color-background-active);font-size:15px;font-weight:var(--ifm-button-font-weight);margin:0 0 .2rem -.5rem;padding:.6rem 1.5rem;position:relative;text-align:left;top:-.5rem;width:calc(100% + 1rem)}.navbar-sidebar__close{display:flex;margin-left:auto}.pagination{column-gap:var(--ifm-pagination-page-spacing);display:flex;font-size:var(--ifm-pagination-font-size);padding-left:0}.pagination--sm{--ifm-pagination-font-size:0.8rem;--ifm-pagination-padding-horizontal:0.8rem;--ifm-pagination-padding-vertical:0.2rem}.pagination--lg{--ifm-pagination-font-size:1.2rem;--ifm-pagination-padding-horizontal:1.2rem;--ifm-pagination-padding-vertical:0.3rem}.pagination__item{display:inline-flex}.pagination__item>span{padding:var(--ifm-pagination-padding-vertical)}.pagination__item--active .pagination__link{color:var(--ifm-pagination-color-active)}.pagination__item--active .pagination__link,.pagination__item:not(.pagination__item--active):hover .pagination__link{background:var(--ifm-pagination-item-active-background)}.pagination__item--disabled,.pagination__item[disabled]{opacity:.25;pointer-events:none}.pagination__link{border-radius:var(--ifm-pagination-border-radius);color:var(--ifm-font-color-base);display:inline-block;padding:var(--ifm-pagination-padding-vertical) var(--ifm-pagination-padding-horizontal);transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination__link:hover,.sidebarItemLink_mo7H:hover{text-decoration:none}.pagination-nav{grid-gap:var(--ifm-spacing-horizontal);display:grid;gap:var(--ifm-spacing-horizontal);grid-template-columns:repeat(2,1fr)}.pagination-nav__link{border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-pagination-nav-border-radius);display:block;height:100%;line-height:var(--ifm-heading-line-height);padding:var(--ifm-global-spacing);transition:border-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav__link:hover{border-color:var(--ifm-pagination-nav-color-hover);text-decoration:none}.DocSearch-Hit[aria-selected=true] mark,.content_knG7 a{text-decoration:underline}.pagination-nav__link--next{grid-column:2/3;text-align:right}.pagination-nav__label{font-size:var(--ifm-h4-font-size);font-weight:var(--ifm-heading-font-weight);word-break:break-word}.pagination-nav__link--prev .pagination-nav__label:before{content:"« "}.pagination-nav__link--next .pagination-nav__label:after{content:" »"}.pagination-nav__sublabel{color:var(--ifm-color-content-secondary);font-size:var(--ifm-h5-font-size);font-weight:var(--ifm-font-weight-semibold);margin-bottom:.25rem}.pills__item,.sidebarItemTitle_pO2u,.tabs{font-weight:var(--ifm-font-weight-bold)}.pills{display:flex;gap:var(--ifm-pills-spacing);padding-left:0}.pills__item{border-radius:.5rem;cursor:pointer;display:inline-block;padding:.25rem 1rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs,:not(.containsTaskList_mC6p>li)>.containsTaskList_mC6p{padding-left:0}.pills__item--active{color:var(--ifm-pills-color-active)}.pills__item--active,.pills__item:not(.pills__item--active):hover{background:var(--ifm-pills-color-background-active)}.pills--block{justify-content:stretch}.pills--block .pills__item{flex-grow:1;text-align:center}.tabs{color:var(--ifm-tabs-color);display:flex;margin-bottom:0;overflow-x:auto}.tabs__item{border-bottom:3px solid transparent;border-radius:var(--ifm-global-radius);cursor:pointer;display:inline-flex;padding:var(--ifm-tabs-padding-vertical) var(--ifm-tabs-padding-horizontal);transition:background-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs__item--active{border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.tabs__item:hover{background-color:var(--ifm-hover-overlay)}.tabs--block{justify-content:stretch}.tabs--block .tabs__item{flex-grow:1;justify-content:center}html[data-theme=dark]{--ifm-color-scheme:dark;--ifm-color-emphasis-0:var(--ifm-color-gray-1000);--ifm-color-emphasis-100:var(--ifm-color-gray-900);--ifm-color-emphasis-200:var(--ifm-color-gray-800);--ifm-color-emphasis-300:var(--ifm-color-gray-700);--ifm-color-emphasis-400:var(--ifm-color-gray-600);--ifm-color-emphasis-600:var(--ifm-color-gray-400);--ifm-color-emphasis-700:var(--ifm-color-gray-300);--ifm-color-emphasis-800:var(--ifm-color-gray-200);--ifm-color-emphasis-900:var(--ifm-color-gray-100);--ifm-color-emphasis-1000:var(--ifm-color-gray-0);--ifm-background-color:#1b1b1d;--ifm-background-surface-color:#242526;--ifm-hover-overlay:hsla(0,0%,100%,.05);--ifm-color-content:#e3e3e3;--ifm-color-content-secondary:#fff;--ifm-breadcrumb-separator-filter:invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(99%) contrast(95%);--ifm-code-background:hsla(0,0%,100%,.1);--ifm-scrollbar-track-background-color:#444;--ifm-scrollbar-thumb-background-color:#686868;--ifm-scrollbar-thumb-hover-background-color:#7a7a7a;--ifm-table-stripe-background:hsla(0,0%,100%,.07);--ifm-toc-border-color:var(--ifm-color-emphasis-200);--ifm-color-primary-contrast-background:#102445;--ifm-color-primary-contrast-foreground:#ebf2fc;--ifm-color-secondary-contrast-background:#474748;--ifm-color-secondary-contrast-foreground:#fdfdfe;--ifm-color-success-contrast-background:#003100;--ifm-color-success-contrast-foreground:#e6f6e6;--ifm-color-info-contrast-background:#193c47;--ifm-color-info-contrast-foreground:#eef9fd;--ifm-color-warning-contrast-background:#4d3800;--ifm-color-warning-contrast-foreground:#fff8e6;--ifm-color-danger-contrast-background:#4b1113;--ifm-color-danger-contrast-foreground:#ffebec;--docsearch-text-color:#f5f6f7;--docsearch-container-background:rgba(9,10,17,.8);--docsearch-modal-background:#15172a;--docsearch-modal-shadow:inset 1px 1px 0 0 #2c2e40,0 3px 8px 0 #000309;--docsearch-searchbox-background:#090a11;--docsearch-searchbox-focus-background:#000;--docsearch-hit-color:#bec3c9;--docsearch-hit-shadow:none;--docsearch-hit-background:#090a11;--docsearch-key-gradient:linear-gradient(-26.5deg,#565872,#31355b);--docsearch-key-shadow:inset 0 -2px 0 0 #282d55,inset 0 0 1px 1px #51577d,0 2px 2px 0 rgba(3,4,9,.3);--docsearch-footer-background:#1e2136;--docsearch-footer-shadow:inset 0 1px 0 0 rgba(73,76,106,.5),0 -4px 8px 0 rgba(0,0,0,.2);--docsearch-logo-color:#fff;--docsearch-muted-color:#7f8497}#nprogress .bar{background:var(--docusaurus-progress-bar-color);height:2px;left:0;position:fixed;top:0;width:100%;z-index:1031}#nprogress .peg{box-shadow:0 0 10px var(--docusaurus-progress-bar-color),0 0 5px var(--docusaurus-progress-bar-color);height:100%;opacity:1;position:absolute;right:0;transform:rotate(3deg) translateY(-4px);width:100px}.docusaurus-highlight-code-line{background-color:rgba(0,0,0,.1);display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}html[data-theme=dark] .docusaurus-highlight-code-line{background-color:rgba(0,0,0,.3)}body:not(.navigation-with-keyboard) :not(input):focus{outline:0}#docusaurus-base-url-issue-banner-container,.collapseSidebarButton_PEFL,.docSidebarContainer_b6E3,.sidebarLogo_isFc,.themedImage_ToTc,[data-theme=dark] .lightToggleIcon_pyhR,[data-theme=light] .darkToggleIcon_wfgR,html[data-announcement-bar-initially-dismissed=true] .announcementBar_mb4j{display:none}.skipToContent_fXgn{background-color:var(--ifm-background-surface-color);color:var(--ifm-color-emphasis-900);left:100%;padding:calc(var(--ifm-global-spacing)/2) var(--ifm-global-spacing);position:fixed;top:1rem}.skipToContent_fXgn:focus{box-shadow:var(--ifm-global-shadow-md);left:1rem}.closeButton_CVFx{line-height:0;padding:0}.content_knG7{font-size:85%;padding:5px 0;text-align:center}.content_knG7 a{color:inherit}.announcementBar_mb4j{align-items:center;background-color:var(--ifm-color-white);box-shadow:var(--ifm-global-shadow-lw);color:var(--ifm-color-black);display:flex;height:var(--docusaurus-announcement-bar-height)}.announcementBarPlaceholder_vyr4{flex:0 0 10px}.announcementBarClose_gvF7{align-self:stretch;flex:0 0 30px}.toggle_vylO{height:2rem;width:2rem}.toggleButton_gllP{align-items:center;border-radius:50%;display:flex;height:100%;justify-content:center;transition:background var(--ifm-transition-fast);width:100%}.toggleButton_gllP:hover{background:var(--ifm-color-emphasis-200)}.toggleButtonDisabled_aARS{cursor:not-allowed}[data-theme=dark] .themedImage--dark_i4oU,[data-theme=light] .themedImage--light_HNdA{display:initial}.iconExternalLink_nPIU{margin-left:.3rem}.iconLanguage_nlXk{margin-right:5px;vertical-align:text-bottom}.navbarHideable_m1mJ{transition:transform var(--ifm-transition-fast) ease}.navbarHidden_jGov{transform:translate3d(0,calc(-100% - 2px),0)}.footerLogoLink_BH7S{opacity:.5;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.footerLogoLink_BH7S:hover,.hash-link:focus,:hover>.hash-link{opacity:1}.mainWrapper_z2l0{flex:1 0 auto}.docusaurus-mt-lg{margin-top:3rem}#__docusaurus{display:flex;flex-direction:column;min-height:100%}.sidebar_re4s{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:sticky;top:calc(var(--ifm-navbar-height) + 2rem)}.sidebarItemTitle_pO2u{font-size:var(--ifm-h3-font-size)}.container_mt6G,.sidebarItemList_Yudw{font-size:.9rem}.sidebarItem__DBe{margin-top:.7rem}.sidebarItemLink_mo7H{color:var(--ifm-font-color-base);display:block}.sidebarItemLinkActive_I1ZP{color:var(--ifm-color-primary)!important}.searchQueryInput_u2C7,.searchVersionInput_m0Ui{background:var(--docsearch-searchbox-focus-background);border:2px solid var(--ifm-toc-border-color);border-radius:var(--ifm-global-radius);color:var(--docsearch-text-color);font:var(--ifm-font-size-base) var(--ifm-font-family-base);margin-bottom:.5rem;padding:.8rem;transition:border var(--ifm-transition-fast) ease;width:100%}.searchQueryInput_u2C7:focus,.searchVersionInput_m0Ui:focus{border-color:var(--docsearch-primary-color);outline:0}.searchQueryInput_u2C7::placeholder{color:var(--docsearch-muted-color)}.searchResultsColumn_JPFH{font-size:.9rem;font-weight:700}.algoliaLogo_rT1R{max-width:150px}.algoliaLogoPathFill_WdUC{fill:var(--ifm-font-color-base)}.searchResultItem_Tv2o{border-bottom:1px solid var(--ifm-toc-border-color);padding:1rem 0}.searchResultItemHeading_KbCB{font-weight:400;margin-bottom:0}.searchResultItemPath_lhe1{--ifm-breadcrumb-separator-size-multiplier:1;color:var(--ifm-color-content-secondary);font-size:.8rem}.searchResultItemSummary_AEaO{font-style:italic;margin:.5rem 0 0}.loadingSpinner_XVxU{animation:1s linear infinite a;border:.4em solid #eee;border-radius:50%;border-top:.4em solid var(--ifm-color-primary);height:3rem;margin:0 auto;width:3rem}@keyframes a{to{transform:rotate(1turn)}}.loader_vvXV{margin-top:2rem}.search-result-match{background:rgba(255,215,142,.25);color:var(--docsearch-hit-color);padding:.09em 0}.backToTopButton_sjWU{background-color:var(--ifm-color-emphasis-200);border-radius:50%;bottom:1.3rem;box-shadow:var(--ifm-global-shadow-lw);height:3rem;opacity:0;position:fixed;right:1.3rem;transform:scale(0);transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default);visibility:hidden;width:3rem;z-index:calc(var(--ifm-z-index-fixed) - 1)}.backToTopButton_sjWU:after{background-color:var(--ifm-color-emphasis-1000);content:" ";display:inline-block;height:100%;-webkit-mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;width:100%}.backToTopButtonShow_xfvO{opacity:1;transform:scale(1);visibility:visible}[data-theme=dark]:root{--docusaurus-collapse-button-bg:hsla(0,0%,100%,.05);--docusaurus-collapse-button-bg-hover:hsla(0,0%,100%,.1)}.docMainContainer_gTbr,.docPage__5DB{display:flex;width:100%}.heroBanner_qdFl{animation:30s infinite b;background:linear-gradient(270deg,#264c98,#1b1f28);background-size:400% 400%;overflow:hidden;padding:4rem 0;position:relative;text-align:center}@keyframes b{0%,to{background-position:0 50%}50%{background-position:100% 50%}}.buttons_AeoN,.mdxPageWrapper_j9I6{justify-content:center}.DocSearch-Button,.DocSearch-Button-Container,.buttons_AeoN,.features_xdhU{align-items:center;display:flex}.features_xdhU{padding:2rem 0;width:100%}.features_xdhU .feature_eSJM{background-color:#fafafa;border-radius:.5rem;margin:1rem 0;padding:1rem}.featureSvg__8YW{height:200px;width:200px}.authorCol_Hf19{flex-grow:1!important;max-width:inherit!important}.imageOnlyAuthorRow_pa_O{display:flex;flex-flow:row wrap}.imageOnlyAuthorCol_G86a{margin-left:.3rem;margin-right:.3rem}.DocSearch-Button{background:var(--docsearch-searchbox-background);border:0;border-radius:40px;color:var(--docsearch-muted-color);cursor:pointer;font-weight:500;height:36px;justify-content:space-between;padding:0 8px;-webkit-user-select:none;user-select:none}.DocSearch-Button:active,.DocSearch-Button:focus,.DocSearch-Button:hover{background:var(--docsearch-searchbox-focus-background);box-shadow:var(--docsearch-searchbox-shadow);color:var(--docsearch-text-color);outline:0}.DocSearch-Search-Icon{stroke-width:1.6}.DocSearch-Hit-Tree,.DocSearch-Hit-action,.DocSearch-Hit-icon,.DocSearch-Reset{stroke-width:var(--docsearch-icon-stroke-width)}.DocSearch-Button .DocSearch-Search-Icon{color:var(--docsearch-text-color)}.DocSearch-Button-Placeholder{font-size:1rem;padding:0 12px 0 6px}.DocSearch-Input,.DocSearch-Link{-webkit-appearance:none;font:inherit}.DocSearch-Button-Keys{display:flex;min-width:calc(40px + .8em)}.DocSearch-Button-Key{align-items:center;background:var(--docsearch-key-gradient);border:0;border-radius:3px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);display:flex;height:18px;justify-content:center;margin-right:.4em;padding:0 0 2px;position:relative;top:-1px;width:20px}.DocSearch--active{overflow:hidden!important}.DocSearch-Container{background-color:var(--docsearch-container-background);height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:200}.DocSearch-Container a{text-decoration:none}.DocSearch-Link{appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;margin:0;padding:0}.DocSearch-Modal{background:var(--docsearch-modal-background);border-radius:6px;box-shadow:var(--docsearch-modal-shadow);flex-direction:column;margin:60px auto auto;max-width:var(--docsearch-modal-width);position:relative}.DocSearch-SearchBar{display:flex;padding:var(--docsearch-spacing) var(--docsearch-spacing) 0}.DocSearch-Form{align-items:center;background:var(--docsearch-searchbox-focus-background);border-radius:4px;box-shadow:var(--docsearch-searchbox-shadow);display:flex;height:var(--docsearch-searchbox-height);margin:0;padding:0 var(--docsearch-spacing);position:relative;width:100%}.DocSearch-Input{appearance:none;background:0 0;border:0;color:var(--docsearch-text-color);flex:1;font-size:1.2em;height:100%;outline:0;padding:0 0 0 8px;width:80%}.DocSearch-Hit-action-button,.DocSearch-Reset{-webkit-appearance:none;border:0;cursor:pointer}.DocSearch-Input::placeholder{color:var(--docsearch-muted-color);opacity:1}.DocSearch-Input::-webkit-search-cancel-button,.DocSearch-Input::-webkit-search-decoration,.DocSearch-Input::-webkit-search-results-button,.DocSearch-Input::-webkit-search-results-decoration{display:none}.DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{margin:0;padding:0}.DocSearch-Container--Stalled .DocSearch-LoadingIndicator,.DocSearch-MagnifierLabel,.DocSearch-Reset{align-items:center;color:var(--docsearch-highlight-color);display:flex;justify-content:center}.DocSearch-Cancel,.DocSearch-Container--Stalled .DocSearch-MagnifierLabel,.DocSearch-LoadingIndicator,.DocSearch-Reset[hidden]{display:none}.DocSearch-Reset{animation:.1s ease-in forwards c;appearance:none;background:none;border-radius:50%;color:var(--docsearch-icon-color);padding:2px;right:0}.DocSearch-Help,.DocSearch-HitsFooter,.DocSearch-Label{color:var(--docsearch-muted-color)}.DocSearch-Reset:focus{outline:0}.DocSearch-Reset:hover{color:var(--docsearch-highlight-color)}.DocSearch-LoadingIndicator svg,.DocSearch-MagnifierLabel svg{height:24px;width:24px}.DocSearch-Dropdown{max-height:calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height));min-height:var(--docsearch-spacing);overflow-y:auto;overflow-y:overlay;padding:0 var(--docsearch-spacing);scrollbar-color:var(--docsearch-muted-color) var(--docsearch-modal-background);scrollbar-width:thin}.DocSearch-Dropdown::-webkit-scrollbar{width:12px}.DocSearch-Dropdown::-webkit-scrollbar-track{background:0 0}.DocSearch-Dropdown::-webkit-scrollbar-thumb{background-color:var(--docsearch-muted-color);border:3px solid var(--docsearch-modal-background);border-radius:20px}.DocSearch-Dropdown ul{list-style:none;margin:0;padding:0}.DocSearch-Label{font-size:.75em;line-height:1.6em}.DocSearch-Help{font-size:.9em;margin:0;-webkit-user-select:none;user-select:none}.DocSearch-Title{font-size:1.2em}.DocSearch-Logo a{display:flex}.DocSearch-Logo svg{color:var(--docsearch-logo-color);margin-left:8px}.DocSearch-Hits:last-of-type{margin-bottom:24px}.DocSearch-Hits mark{background:none;color:var(--docsearch-highlight-color)}.DocSearch-HitsFooter{display:flex;font-size:.85em;justify-content:center;margin-bottom:var(--docsearch-spacing);padding:var(--docsearch-spacing)}.DocSearch-HitsFooter a{border-bottom:1px solid;color:inherit}.DocSearch-Hit{border-radius:4px;display:flex;padding-bottom:4px;position:relative}.DocSearch-Hit--deleting{opacity:0;transition:.25s linear}.DocSearch-Hit--favoriting{transform:scale(0);transform-origin:top center;transition:.25s linear .25s}.DocSearch-Hit a{background:var(--docsearch-hit-background);border-radius:4px;box-shadow:var(--docsearch-hit-shadow);display:block;padding-left:var(--docsearch-spacing);width:100%}.DocSearch-Hit-source{background:var(--docsearch-modal-background);color:var(--docsearch-highlight-color);font-size:.85em;font-weight:600;line-height:32px;margin:0 -4px;padding:8px 4px 0;position:sticky;top:0;z-index:10}.DocSearch-Hit-Tree{color:var(--docsearch-muted-color);height:var(--docsearch-hit-height);opacity:.5;width:24px}.DocSearch-Hit[aria-selected=true] a{background-color:var(--docsearch-highlight-color)}.DocSearch-Hit-Container{align-items:center;color:var(--docsearch-hit-color);display:flex;flex-direction:row;height:var(--docsearch-hit-height);padding:0 var(--docsearch-spacing) 0 0}.DocSearch-Hit-icon{height:20px;width:20px}.DocSearch-Hit-action,.DocSearch-Hit-icon{color:var(--docsearch-muted-color)}.DocSearch-Hit-action{align-items:center;display:flex;height:22px;width:22px}.DocSearch-Hit-action svg{display:block;height:18px;width:18px}.DocSearch-Hit-action+.DocSearch-Hit-action{margin-left:6px}.DocSearch-Hit-action-button{appearance:none;background:none;border-radius:50%;color:inherit;padding:2px}svg.DocSearch-Hit-Select-Icon{display:none}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Select-Icon,.tocCollapsibleContent_vkbj a{display:block}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:background-color .1s ease-in}.DocSearch-Hit-action-button:focus path,.DocSearch-Hit-action-button:hover path{fill:#fff}.DocSearch-Hit-content-wrapper{display:flex;flex:1 1 auto;flex-direction:column;font-weight:500;justify-content:center;line-height:1.2em;margin:0 8px;overflow-x:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap;width:80%}.DocSearch-Hit-title{font-size:.9em}.DocSearch-Hit-path{color:var(--docsearch-muted-color);font-size:.75em}.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-Tree,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-action,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-icon,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-path,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-text,.DocSearch-Hit[aria-selected=true] .DocSearch-Hit-title,.DocSearch-Hit[aria-selected=true] mark{color:var(--docsearch-hit-active-color)!important}.DocSearch-ErrorScreen,.DocSearch-NoResults,.DocSearch-StartScreen{font-size:.9em;margin:0 auto;padding:36px 0;text-align:center;width:80%}.DocSearch-Screen-Icon{color:var(--docsearch-muted-color);padding-bottom:12px}.DocSearch-NoResults-Prefill-List{display:inline-block;padding-bottom:24px;text-align:left}.DocSearch-NoResults-Prefill-List ul{display:inline-block;padding:8px 0 0}.DocSearch-NoResults-Prefill-List li{list-style-position:inside;list-style-type:"» "}.DocSearch-Prefill{-webkit-appearance:none;appearance:none;background:none;border:0;border-radius:1em;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;font-size:1em;font-weight:700;padding:0}.DocSearch-Prefill:focus,.DocSearch-Prefill:hover{outline:0;text-decoration:underline}.DocSearch-Footer{align-items:center;background:var(--docsearch-footer-background);border-radius:0 0 8px 8px;box-shadow:var(--docsearch-footer-shadow);display:flex;flex-direction:row-reverse;flex-shrink:0;height:var(--docsearch-footer-height);justify-content:space-between;padding:0 var(--docsearch-spacing);position:relative;-webkit-user-select:none;user-select:none;width:100%;z-index:300}.DocSearch-Commands li,.DocSearch-Commands-Key{align-items:center;display:flex}.DocSearch-Commands{color:var(--docsearch-muted-color);display:flex;list-style:none;margin:0;padding:0}.DocSearch-Commands li:not(:last-of-type){margin-right:.8em}.DocSearch-Commands-Key{background:var(--docsearch-key-gradient);border:0;border-radius:2px;box-shadow:var(--docsearch-key-shadow);color:var(--docsearch-muted-color);height:18px;justify-content:center;margin-right:.4em;padding:0 0 1px;width:20px}.buttonGroup__atx button,.codeBlockContainer_Ckt0{background:var(--prism-background-color);color:var(--prism-color)}@keyframes c{0%{opacity:0}to{opacity:1}}.DocSearch-Button{margin:0;transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.DocSearch-Container{z-index:calc(var(--ifm-z-index-fixed) + 1)}.codeBlockContainer_Ckt0{border-radius:var(--ifm-code-border-radius);box-shadow:var(--ifm-global-shadow-lw);margin-bottom:var(--ifm-leading)}.codeBlockContent_biex{border-radius:inherit;direction:ltr;position:relative}.codeBlockTitle_Ktv7{border-bottom:1px solid var(--ifm-color-emphasis-300);border-top-left-radius:inherit;border-top-right-radius:inherit;font-size:var(--ifm-code-font-size);font-weight:500;padding:.75rem var(--ifm-pre-padding)}.codeBlock_bY9V{--ifm-pre-background:var(--prism-background-color);margin:0;padding:0}.codeBlockTitle_Ktv7+.codeBlockContent_biex .codeBlock_bY9V{border-top-left-radius:0;border-top-right-radius:0}.codeBlockLines_e6Vv{float:left;font:inherit;min-width:100%;padding:var(--ifm-pre-padding)}.codeBlockLinesWithNumbering_o6Pm{display:table;padding:var(--ifm-pre-padding) 0}.buttonGroup__atx{column-gap:.2rem;display:flex;position:absolute;right:calc(var(--ifm-pre-padding)/2);top:calc(var(--ifm-pre-padding)/2)}.buttonGroup__atx button{align-items:center;border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-global-radius);display:flex;line-height:0;opacity:0;padding:.4rem;transition:opacity .2s ease-in-out}.buttonGroup__atx button:focus-visible,.buttonGroup__atx button:hover{opacity:1!important}.theme-code-block:hover .buttonGroup__atx button{opacity:.4}.iconEdit_Z9Sw{margin-right:.3em;vertical-align:sub}:where(:root){--docusaurus-highlighted-code-line-bg:#484d5b}:where([data-theme=dark]){--docusaurus-highlighted-code-line-bg:#646464}.theme-code-block-highlighted-line{background-color:var(--docusaurus-highlighted-code-line-bg);display:block;margin:0 calc(var(--ifm-pre-padding)*-1);padding:0 var(--ifm-pre-padding)}.codeLine_lJS_{counter-increment:a;display:table-row}.codeLineNumber_Tfdd{background:var(--ifm-pre-background);display:table-cell;left:0;overflow-wrap:normal;padding:0 var(--ifm-pre-padding);position:sticky;text-align:right;width:1%}.codeLineNumber_Tfdd:before{content:counter(a);opacity:.4}.codeLineContent_feaV{padding-right:var(--ifm-pre-padding)}.tag_zVej{border:1px solid var(--docusaurus-tag-list-border);transition:border var(--ifm-transition-fast)}.tag_zVej:hover{--docusaurus-tag-list-border:var(--ifm-link-color);text-decoration:none}.tagRegular_sFm0{border-radius:.5rem;font-size:90%;padding:.2rem .5rem .3rem}.tagWithCount_h2kH{align-items:center;border-left:0;display:flex;padding:0 .5rem 0 1rem;position:relative}.tagWithCount_h2kH:after,.tagWithCount_h2kH:before{border:1px solid var(--docusaurus-tag-list-border);content:"";position:absolute;top:50%;transition:inherit}.tagWithCount_h2kH:before{border-bottom:0;border-right:0;height:1.18rem;right:100%;transform:translate(50%,-50%) rotate(-45deg);width:1.18rem}.tagWithCount_h2kH:after{border-radius:50%;height:.5rem;left:0;transform:translateY(-50%);width:.5rem}.tagWithCount_h2kH span{background:var(--ifm-color-secondary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-black);font-size:.7rem;line-height:1.2;margin-left:.3rem;padding:.1rem .4rem}.tag_Nnez{display:inline-block;margin:.5rem .5rem 0 1rem}.theme-code-block:hover .copyButtonCopied_obH4{opacity:1!important}.copyButtonIcons_eSgA{height:1.125rem;position:relative;width:1.125rem}.copyButtonIcon_y97N,.copyButtonSuccessIcon_LjdS{fill:currentColor;height:inherit;left:0;opacity:inherit;position:absolute;top:0;transition:.15s;width:inherit}.copyButtonSuccessIcon_LjdS{color:#00d600;left:50%;opacity:0;top:50%;transform:translate(-50%,-50%) scale(.33)}.copyButtonCopied_obH4 .copyButtonIcon_y97N{opacity:0;transform:scale(.33)}.copyButtonCopied_obH4 .copyButtonSuccessIcon_LjdS{opacity:1;transform:translate(-50%,-50%) scale(1);transition-delay:75ms}.tags_jXut{display:inline}.tag_QGVx{display:inline-block;margin:0 .4rem .5rem 0}.lastUpdated_vwxv{font-size:smaller;font-style:italic;margin-top:.2rem}.tocCollapsibleButton_TO0P{align-items:center;display:flex;font-size:inherit;justify-content:space-between;padding:.4rem .8rem;width:100%}.tocCollapsibleButton_TO0P:after{background:var(--ifm-menu-link-sublist-icon) 50% 50%/2rem 2rem no-repeat;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast);width:1.25rem}.tocCollapsibleButtonExpanded_MG3E:after,.tocCollapsibleExpanded_sAul{transform:none}.tocCollapsible_ETCw{background-color:var(--ifm-menu-color-background-active);border-radius:var(--ifm-global-radius);margin:1rem 0}.tocCollapsibleContent_vkbj>ul{border-left:none;border-top:1px solid var(--ifm-color-emphasis-300);font-size:15px;padding:.2rem 0}.tocCollapsibleContent_vkbj ul li{margin:.4rem .8rem}.wordWrapButtonIcon_Bwma{height:1.2rem;width:1.2rem}.details_lb9f{--docusaurus-details-summary-arrow-size:0.38rem;--docusaurus-details-transition:transform 200ms ease;--docusaurus-details-decoration-color:grey}.details_lb9f>summary{cursor:pointer;list-style:none;padding-left:1rem;position:relative}.details_lb9f>summary::-webkit-details-marker{display:none}.details_lb9f>summary:before{border-color:transparent transparent transparent var(--docusaurus-details-decoration-color);border-style:solid;border-width:var(--docusaurus-details-summary-arrow-size);content:"";left:0;position:absolute;top:.45rem;transform:rotate(0);transform-origin:calc(var(--docusaurus-details-summary-arrow-size)/2) 50%;transition:var(--docusaurus-details-transition)}.collapsibleContent_i85q{border-top:1px solid var(--docusaurus-details-decoration-color);margin-top:1rem;padding-top:1rem}.details_b_Ee{--docusaurus-details-decoration-color:var(--ifm-alert-border-color);--docusaurus-details-transition:transform var(--ifm-transition-fast) ease;border:1px solid var(--ifm-alert-border-color);margin:0 0 var(--ifm-spacing-vertical)}.anchorWithStickyNavbar_LWe7{scroll-margin-top:calc(var(--ifm-navbar-height) + .5rem)}.anchorWithHideOnScrollNavbar_WYt5{scroll-margin-top:.5rem}.hash-link{opacity:0;padding-left:.5rem;transition:opacity var(--ifm-transition-fast);-webkit-user-select:none;user-select:none}.hash-link:before{content:"#"}.containsTaskList_mC6p{list-style:none}.img_ev3q{height:auto}.admonition_LlT9{margin-bottom:1em}.admonitionHeading_tbUL{font:var(--ifm-heading-font-weight) var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.3rem}.admonitionHeading_tbUL code{text-transform:none}.admonitionIcon_kALy{display:inline-block;margin-right:.4em;vertical-align:middle}.admonitionIcon_kALy svg{fill:var(--ifm-alert-foreground-color);display:inline-block;height:1.6em;width:1.6em}.blogPostFooterDetailsFull_mRVl{flex-direction:column}.tableOfContents_bqdL{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:sticky;top:calc(var(--ifm-navbar-height) + 1rem)}.breadcrumbsContainer_Z_bl{--ifm-breadcrumb-size-multiplier:0.8;margin-bottom:.8rem}.breadcrumbHomeIcon_OVgt{height:1.1rem;position:relative;top:1px;vertical-align:top;width:1.1rem}@media (min-width:997px){.collapseSidebarButton_PEFL,.expandButton_m80_{background-color:var(--docusaurus-collapse-button-bg);position:sticky}:root{--docusaurus-announcement-bar-height:30px}.announcementBarClose_gvF7,.announcementBarPlaceholder_vyr4{flex-basis:50px}.searchBox_ZlJk{padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.collapseSidebarButton_PEFL{border:1px solid var(--ifm-toc-border-color);border-radius:0;bottom:0;display:block!important;height:40px}.collapseSidebarButtonIcon_kv0_{margin-top:4px;transform:rotate(180deg)}.expandButtonIcon_BlDH,[dir=rtl] .collapseSidebarButtonIcon_kv0_{transform:rotate(0)}.collapseSidebarButton_PEFL:focus,.collapseSidebarButton_PEFL:hover,.expandButton_m80_:focus,.expandButton_m80_:hover{background-color:var(--docusaurus-collapse-button-bg-hover)}.menuHtmlItem_M9Kj{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu_SIkG{flex-grow:1;padding:.5rem}@supports (scrollbar-gutter:stable){.menu_SIkG{padding:.5rem 0 .5rem .5rem;scrollbar-gutter:stable}}.menuWithAnnouncementBar_GW3s{margin-bottom:var(--docusaurus-announcement-bar-height)}.sidebar_njMd{display:flex;flex-direction:column;height:100%;max-height:100vh;padding-top:var(--ifm-navbar-height);position:sticky;top:0;transition:opacity 50ms;width:var(--doc-sidebar-width)}.sidebarWithHideableNavbar_wUlq{padding-top:0}.sidebarHidden_VK0M{height:0;opacity:0;overflow:hidden;visibility:hidden}.sidebarLogo_isFc{align-items:center;color:inherit!important;display:flex!important;margin:0 var(--ifm-navbar-padding-horizontal);max-height:var(--ifm-navbar-height);min-height:var(--ifm-navbar-height);text-decoration:none!important}.sidebarLogo_isFc img{height:2rem;margin-right:.5rem}.expandButton_m80_{align-items:center;display:flex;height:100%;justify-content:center;max-height:100vh;top:0;transition:background-color var(--ifm-transition-fast) ease}[dir=rtl] .expandButtonIcon_BlDH{transform:rotate(180deg)}.docSidebarContainer_b6E3{border-right:1px solid var(--ifm-toc-border-color);-webkit-clip-path:inset(0);clip-path:inset(0);display:block;margin-top:calc(var(--ifm-navbar-height)*-1);transition:width var(--ifm-transition-fast) ease;width:var(--doc-sidebar-width);will-change:width}.docSidebarContainerHidden_b3ry{cursor:pointer;width:var(--doc-sidebar-hidden-width)}.docMainContainer_gTbr{flex-grow:1;max-width:calc(100% - var(--doc-sidebar-width))}.docMainContainerEnhanced_Uz_u{max-width:calc(100% - var(--doc-sidebar-hidden-width))}.docItemWrapperEnhanced_czyv{max-width:calc(var(--ifm-container-width) + var(--doc-sidebar-width))!important}.lastUpdated_vwxv{text-align:right}.tocMobile_ITEo{display:none}.docItemCol_VOVn{max-width:75%!important}}@media (min-width:1440px){.container{max-width:var(--ifm-container-width-xl)}}@media (max-width:996px){.col{--ifm-col-width:100%;flex-basis:var(--ifm-col-width);margin-left:0}.footer{--ifm-footer-padding-horizontal:0}.colorModeToggle_DEke,.footer__link-separator,.navbar__item,.sidebar_re4s,.tableOfContents_bqdL{display:none}.footer__col{margin-bottom:calc(var(--ifm-spacing-vertical)*3)}.footer__link-item{display:block}.hero{padding-left:0;padding-right:0}.navbar>.container,.navbar>.container-fluid{padding:0}.navbar__toggle{display:inherit}.navbar__search-input{width:9rem}.pills--block,.tabs--block{flex-direction:column}.searchBox_ZlJk{position:absolute;right:var(--ifm-navbar-padding-horizontal)}.docItemContainer_F8PC{padding:0 .3rem}}@media only screen and (max-width:996px){.searchQueryColumn_RTkw,.searchResultsColumn_JPFH{max-width:60%!important}.searchLogoColumn_rJIA,.searchVersionColumn_ypXd{max-width:40%!important}.searchLogoColumn_rJIA{padding-left:0!important}}@media screen and (max-width:966px){.heroBanner_qdFl{padding:2rem}}@media (max-width:768px){.DocSearch-Button-Keys,.DocSearch-Button-Placeholder,.DocSearch-Commands,.DocSearch-Hit-Tree{display:none}:root{--docsearch-spacing:10px;--docsearch-footer-height:40px}.DocSearch-Dropdown{height:100%;max-height:calc(var(--docsearch-vh,1vh)*100 - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))}.DocSearch-Container{height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh,1vh)*100);position:absolute}.DocSearch-Footer{border-radius:0;bottom:0;position:absolute}.DocSearch-Hit-content-wrapper{display:flex;position:relative;width:80%}.DocSearch-Modal{border-radius:0;box-shadow:none;height:100vh;height:-webkit-fill-available;height:calc(var(--docsearch-vh,1vh)*100);margin:0;max-width:100%;width:100%}.DocSearch-Cancel{-webkit-appearance:none;appearance:none;background:none;border:0;color:var(--docsearch-highlight-color);cursor:pointer;display:inline-block;flex:none;font:inherit;font-size:1em;font-weight:500;margin-left:var(--docsearch-spacing);outline:0;overflow:hidden;padding:0;-webkit-user-select:none;user-select:none;white-space:nowrap}}@media (max-width:576px){.markdown h1:first-child{--ifm-h1-font-size:2rem}.markdown>h2{--ifm-h2-font-size:1.5rem}.markdown>h3{--ifm-h3-font-size:1.25rem}.title_f1Hy{font-size:2rem}}@media screen and (max-width:576px){.searchQueryColumn_RTkw{max-width:100%!important}.searchVersionColumn_ypXd{max-width:100%!important;padding-left:var(--ifm-spacing-horizontal)!important}}@media (hover:hover){.backToTopButton_sjWU:hover{background-color:var(--ifm-color-emphasis-300)}}@media (pointer:fine){.thin-scrollbar{scrollbar-width:thin}.thin-scrollbar::-webkit-scrollbar{height:var(--ifm-scrollbar-size);width:var(--ifm-scrollbar-size)}.thin-scrollbar::-webkit-scrollbar-track{background:var(--ifm-scrollbar-track-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb{background:var(--ifm-scrollbar-thumb-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--ifm-scrollbar-thumb-hover-background-color)}}@media screen and (prefers-reduced-motion:reduce){.DocSearch-Reset{stroke-width:var(--docsearch-icon-stroke-width);animation:none;-webkit-appearance:none;appearance:none;background:none;border:0;border-radius:50%;color:var(--docsearch-icon-color);cursor:pointer;right:0}.DocSearch-Hit--deleting,.DocSearch-Hit--favoriting{transition:none}.DocSearch-Hit-action-button:focus,.DocSearch-Hit-action-button:hover{background:rgba(0,0,0,.2);transition:none}}@media print{.announcementBar_mb4j,.footer,.menu,.navbar,.pagination-nav,.table-of-contents,.tocMobile_ITEo{display:none}.tabs{page-break-inside:avoid}.codeBlockLines_e6Vv{white-space:pre-wrap}} \ No newline at end of file diff --git a/assets/images/sse-minimal-8e509ce578077ebdc2ca78a9199632e5.png b/assets/images/sse-minimal-8e509ce578077ebdc2ca78a9199632e5.png new file mode 100644 index 0000000..98e4816 Binary files /dev/null and b/assets/images/sse-minimal-8e509ce578077ebdc2ca78a9199632e5.png differ diff --git a/assets/js/01a85c17.66585222.js b/assets/js/01a85c17.66585222.js new file mode 100644 index 0000000..328c75d --- /dev/null +++ b/assets/js/01a85c17.66585222.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[4013],{9058:(e,t,a)=>{a.d(t,{Z:()=>f});var l=a(7294),n=a(6010),r=a(7767),s=a(7524),c=a(9960),i=a(5999);const m="sidebar_re4s",o="sidebarItemTitle_pO2u",u="sidebarItemList_Yudw",g="sidebarItem__DBe",d="sidebarItemLink_mo7H",E="sidebarItemLinkActive_I1ZP";function b(e){let{sidebar:t}=e;return l.createElement("aside",{className:"col col--3"},l.createElement("nav",{className:(0,n.Z)(m,"thin-scrollbar"),"aria-label":(0,i.I)({id:"theme.blog.sidebar.navAriaLabel",message:"Blog recent posts navigation",description:"The ARIA label for recent posts in the blog sidebar"})},l.createElement("div",{className:(0,n.Z)(o,"margin-bottom--md")},t.title),l.createElement("ul",{className:(0,n.Z)(u,"clean-list")},t.items.map((e=>l.createElement("li",{key:e.permalink,className:g},l.createElement(c.Z,{isNavLink:!0,to:e.permalink,className:d,activeClassName:E},e.title)))))))}var p=a(3102);function h(e){let{sidebar:t}=e;return l.createElement("ul",{className:"menu__list"},t.items.map((e=>l.createElement("li",{key:e.permalink,className:"menu__list-item"},l.createElement(c.Z,{isNavLink:!0,to:e.permalink,className:"menu__link",activeClassName:"menu__link--active"},e.title)))))}function k(e){return l.createElement(p.Zo,{component:h,props:e})}function N(e){let{sidebar:t}=e;const a=(0,s.i)();return null!=t&&t.items.length?"mobile"===a?l.createElement(k,{sidebar:t}):l.createElement(b,{sidebar:t}):null}function f(e){const{sidebar:t,toc:a,children:s,...c}=e,i=t&&t.items.length>0;return l.createElement(r.Z,c,l.createElement("div",{className:"container margin-vert--lg"},l.createElement("div",{className:"row"},l.createElement(N,{sidebar:t}),l.createElement("main",{className:(0,n.Z)("col",{"col--7":i,"col--9 col--offset-1":!i}),itemScope:!0,itemType:"http://schema.org/Blog"},s),a&&l.createElement("div",{className:"col col--2"},a))))}},4524:(e,t,a)=>{a.r(t),a.d(t,{default:()=>u});var l=a(7294),n=a(6010),r=a(5155),s=a(833),c=a(5281),i=a(9058),m=a(6090),o=a(197);function u(e){let{tags:t,sidebar:a}=e;const u=(0,r.M)();return l.createElement(s.FG,{className:(0,n.Z)(c.k.wrapper.blogPages,c.k.page.blogTagsListPage)},l.createElement(s.d,{title:u}),l.createElement(o.Z,{tag:"blog_tags_list"}),l.createElement(i.Z,{sidebar:a},l.createElement("h1",null,u),l.createElement(m.Z,{tags:t})))}},3008:(e,t,a)=>{a.d(t,{Z:()=>m});var l=a(7294),n=a(6010),r=a(9960);const s="tag_zVej",c="tagRegular_sFm0",i="tagWithCount_h2kH";function m(e){let{permalink:t,label:a,count:m}=e;return l.createElement(r.Z,{href:t,className:(0,n.Z)(s,m?i:c)},a,m&&l.createElement("span",null,m))}},6090:(e,t,a)=>{a.d(t,{Z:()=>i});var l=a(7294),n=a(5155),r=a(3008);const s="tag_Nnez";function c(e){let{letterEntry:t}=e;return l.createElement("article",null,l.createElement("h2",null,t.letter),l.createElement("ul",{className:"padding--none"},t.tags.map((e=>l.createElement("li",{key:e.permalink,className:s},l.createElement(r.Z,e))))),l.createElement("hr",null))}function i(e){let{tags:t}=e;const a=(0,n.P)(t);return l.createElement("section",{className:"margin-vert--lg"},a.map((e=>l.createElement(c,{key:e.letter,letterEntry:e}))))}},5155:(e,t,a)=>{a.d(t,{M:()=>n,P:()=>r});var l=a(5999);const n=()=>(0,l.I)({id:"theme.tags.tagsPageTitle",message:"Tags",description:"The title of the tag list page"});function r(e){const t={};return Object.values(e).forEach((e=>{const a=function(e){return e[0].toUpperCase()}(e.label);t[a]??=[],t[a].push(e)})),Object.entries(t).sort(((e,t)=>{let[a]=e,[l]=t;return a.localeCompare(l)})).map((e=>{let[t,a]=e;return{letter:t,tags:a.sort(((e,t)=>e.label.localeCompare(t.label)))}}))}}}]); \ No newline at end of file diff --git a/assets/js/01a85c17.7d0034a3.js b/assets/js/01a85c17.7d0034a3.js deleted file mode 100644 index 28d960e..0000000 --- a/assets/js/01a85c17.7d0034a3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[4013],{8665:function(e,t,a){a.d(t,{Z:function(){return f}});var r=a(102),n=a(7294),l=a(6010),c=a(8882),s=a(9960),i="sidebar_q+wC",m="sidebarItemTitle_9G5K",o="sidebarItemList_6T4b",u="sidebarItem_cjdF",g="sidebarItemLink_zyXk",d="sidebarItemLinkActive_wcJs",b=a(5999);function v(e){var t=e.sidebar;return 0===t.items.length?null:n.createElement("nav",{className:(0,l.Z)(i,"thin-scrollbar"),"aria-label":(0,b.I)({id:"theme.blog.sidebar.navAriaLabel",message:"Blog recent posts navigation",description:"The ARIA label for recent posts in the blog sidebar"})},n.createElement("div",{className:(0,l.Z)(m,"margin-bottom--md")},t.title),n.createElement("ul",{className:o},t.items.map((function(e){return n.createElement("li",{key:e.permalink,className:u},n.createElement(s.Z,{isNavLink:!0,to:e.permalink,className:g,activeClassName:d},e.title))}))))}var E=["sidebar","toc","children"];var f=function(e){var t=e.sidebar,a=e.toc,s=e.children,i=(0,r.Z)(e,E),m=t&&t.items.length>0;return n.createElement(c.Z,i,n.createElement("div",{className:"container margin-vert--lg"},n.createElement("div",{className:"row"},m&&n.createElement("aside",{className:"col col--3"},n.createElement(v,{sidebar:t})),n.createElement("main",{className:(0,l.Z)("col",{"col--7":m,"col--9 col--offset-1":!m}),itemScope:!0,itemType:"http://schema.org/Blog"},s),a&&n.createElement("div",{className:"col col--2"},a))))}},94:function(e,t,a){a.r(t);var r=a(7294),n=a(8665),l=a(3306),c=a(6681);t.default=function(e){var t=e.tags,a=e.sidebar,s=(0,c.MA)();return r.createElement(n.Z,{title:s,wrapperClassName:c.kM.wrapper.blogPages,pageClassName:c.kM.page.blogTagsListPage,searchMetadata:{tag:"blog_tags_list"},sidebar:a},r.createElement("h1",null,s),r.createElement(l.Z,{tags:Object.values(t)}))}},7774:function(e,t,a){a.d(t,{Z:function(){return m}});var r=a(7294),n=a(6010),l=a(9960),c="tag_WK-t",s="tagRegular_LXbV",i="tagWithCount_S5Zl";var m=function(e){var t,a=e.permalink,m=e.name,o=e.count;return r.createElement(l.Z,{href:a,className:(0,n.Z)(c,(t={},t[s]=!o,t[i]=o,t))},m,o&&r.createElement("span",null,o))}},3306:function(e,t,a){a.d(t,{Z:function(){return i}});var r=a(7294),n=a(7774),l=a(6681),c="tag_7kA+";function s(e){var t=e.letterEntry;return r.createElement("article",null,r.createElement("h2",null,t.letter),r.createElement("ul",{className:"padding--none"},t.tags.map((function(e){return r.createElement("li",{key:e.permalink,className:c},r.createElement(n.Z,e))}))),r.createElement("hr",null))}var i=function(e){var t=e.tags,a=(0,l.PZ)(t);return r.createElement("section",{className:"margin-vert--lg"},a.map((function(e){return r.createElement(s,{key:e.letter,letterEntry:e})})))}}}]); \ No newline at end of file diff --git a/assets/js/02493ab9.65b30b90.js b/assets/js/02493ab9.65b30b90.js deleted file mode 100644 index cc06f1a..0000000 --- a/assets/js/02493ab9.65b30b90.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[4761],{7:function(e){e.exports=JSON.parse('{"allTagsPath":"/blog/tags","slug":"/blog/tags/search-engine","name":"search-engine","count":1,"permalink":"/blog/tags/search-engine"}')}}]); \ No newline at end of file diff --git a/assets/js/02493ab9.f7eeecef.js b/assets/js/02493ab9.f7eeecef.js new file mode 100644 index 0000000..0f4d2a7 --- /dev/null +++ b/assets/js/02493ab9.f7eeecef.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[4761],{7:e=>{e.exports=JSON.parse('{"label":"search-engine","permalink":"/blog/tags/search-engine","allTagsPath":"/blog/tags","count":1}')}}]); \ No newline at end of file diff --git a/assets/js/04056cce.ac552ccb.js b/assets/js/04056cce.ac552ccb.js new file mode 100644 index 0000000..2bc72b6 --- /dev/null +++ b/assets/js/04056cce.ac552ccb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[6660],{3905:(e,t,n)=>{n.d(t,{Zo:()=>d,kt:()=>m});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=o.createContext({}),l=function(e){var t=o.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},d=function(e){var t=l(e.components);return o.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},c=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,p=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),c=l(n),m=r,g=c["".concat(p,".").concat(m)]||c[m]||u[m]||i;return n?o.createElement(g,a(a({ref:t},d),{},{components:n})):o.createElement(g,a({ref:t},d))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,a=new Array(i);a[0]=c;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:r,a[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>a,default:()=>u,frontMatter:()=>i,metadata:()=>s,toc:()=>l});var o=n(7462),r=(n(7294),n(3905));const i={sidebar_label:"Spot Instance Node Pools"},a="Spot Instance Node Pools",s={unversionedId:"devops/kubernetes/spot-instance-node-pools",id:"devops/kubernetes/spot-instance-node-pools",title:"Spot Instance Node Pools",description:"Setting up spot instance node pools is a great way to save money on stateless applications",source:"@site/docs/devops/kubernetes/spot-instance-node-pools.md",sourceDirName:"devops/kubernetes",slug:"/devops/kubernetes/spot-instance-node-pools",permalink:"/docs/devops/kubernetes/spot-instance-node-pools",draft:!1,editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/docs/devops/kubernetes/spot-instance-node-pools.md",tags:[],version:"current",frontMatter:{sidebar_label:"Spot Instance Node Pools"},sidebar:"devopsSidebar",previous:{title:"Intro",permalink:"/docs/devops/intro"},next:{title:"k8s",permalink:"/docs/devops/k8s"}},p={},l=[{value:"Setting Up Node Taints",id:"setting-up-node-taints",level:2},{value:"Setting Up Pod Toleration",id:"setting-up-pod-toleration",level:2},{value:"Pod Toleration",id:"pod-toleration",level:3},{value:"DaemonSet Toleration",id:"daemonset-toleration",level:3},{value:"Setting Up Pod Disruption Budgets",id:"setting-up-pod-disruption-budgets",level:2},{value:"Setting Up Termination Handling",id:"setting-up-termination-handling",level:2},{value:"Setting Up K8s Cron Shutdown Cleanup",id:"setting-up-k8s-cron-shutdown-cleanup",level:2}],d={toc:l};function u(e){let{components:t,...n}=e;return(0,r.kt)("wrapper",(0,o.Z)({},d,n,{components:t,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"spot-instance-node-pools"},"Spot Instance Node Pools"),(0,r.kt)("p",null,"Setting up spot instance node pools is a great way to save money on stateless applications"),(0,r.kt)("h2",{id:"setting-up-node-taints"},"Setting Up Node Taints"),(0,r.kt)("h2",{id:"setting-up-pod-toleration"},"Setting Up Pod Toleration"),(0,r.kt)("h3",{id:"pod-toleration"},"Pod Toleration"),(0,r.kt)("h3",{id:"daemonset-toleration"},"DaemonSet Toleration"),(0,r.kt)("p",null,"Don't forget to set the daemonset tolerations"),(0,r.kt)("h2",{id:"setting-up-pod-disruption-budgets"},"Setting Up Pod Disruption Budgets"),(0,r.kt)("p",null,"Setting up a pod disruption budget is important due to erratic node shutdown possibility.\nWhat could happen is that nodes that are hosting the applications start terminating, and our application becomes\nunresponsive until the application is re-located to another node.\nTo avoid that situation, for the instances we need to configure pod disruption budget on our helm deployments."),(0,r.kt)("pre",null,(0,r.kt)("code",{parentName:"pre",className:"language-yaml"},"apiVersion: policy/v1\nkind: PodDisruptionBudget\nmetadata:\n name: application-name\nspec:\n maxAvailable: 50%\n selector:\n matchLabels:\n app: application-name\n\n")),(0,r.kt)("p",null,"For more details check out ",(0,r.kt)("a",{parentName:"p",href:"https://kubernetes.io/docs/tasks/run-application/configure-pdb/"},"here")),(0,r.kt)("h2",{id:"setting-up-termination-handling"},"Setting Up Termination Handling"),(0,r.kt)("h2",{id:"setting-up-k8s-cron-shutdown-cleanup"},"Setting Up K8s Cron Shutdown Cleanup"))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/04056cce.c415efdc.js b/assets/js/04056cce.c415efdc.js deleted file mode 100644 index b5a7359..0000000 --- a/assets/js/04056cce.c415efdc.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[6660],{3905:function(e,t,n){n.d(t,{Zo:function(){return d},kt:function(){return m}});var o=n(7294);function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function a(e){for(var t=1;t=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}var p=o.createContext({}),l=function(e){var t=o.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},d=function(e){var t=l(e.components);return o.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},c=o.forwardRef((function(e,t){var n=e.components,r=e.mdxType,i=e.originalType,p=e.parentName,d=s(e,["components","mdxType","originalType","parentName"]),c=l(n),m=r,f=c["".concat(p,".").concat(m)]||c[m]||u[m]||i;return n?o.createElement(f,a(a({ref:t},d),{},{components:n})):o.createElement(f,a({ref:t},d))}));function m(e,t){var n=arguments,r=t&&t.mdxType;if("string"==typeof e||r){var i=n.length,a=new Array(i);a[0]=c;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s.mdxType="string"==typeof e?e:r,a[1]=s;for(var l=2;l{s.exports=JSON.parse('{"label":"k8s","permalink":"/blog/tags/k-8-s","allTagsPath":"/blog/tags","count":1}')}}]); \ No newline at end of file diff --git a/assets/js/0606a637.0b8ee051.js b/assets/js/0606a637.0b8ee051.js new file mode 100644 index 0000000..510ab29 --- /dev/null +++ b/assets/js/0606a637.0b8ee051.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[2648],{3905:(e,t,n)=>{n.d(t,{Zo:()=>p,kt:()=>f});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function a(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var u=r.createContext({}),s=function(e){var t=r.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):a(a({},t),e)),n},p=function(e){var t=s(e.components);return r.createElement(u.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,u=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),d=s(n),f=o,m=d["".concat(u,".").concat(f)]||d[f]||c[f]||i;return n?r.createElement(m,a(a({ref:t},p),{},{components:n})):r.createElement(m,a({ref:t},p))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,a=new Array(i);a[0]=d;var l={};for(var u in t)hasOwnProperty.call(t,u)&&(l[u]=t[u]);l.originalType=e,l.mdxType="string"==typeof e?e:o,a[1]=l;for(var s=2;s{n.r(t),n.d(t,{assets:()=>u,contentTitle:()=>a,default:()=>c,frontMatter:()=>i,metadata:()=>l,toc:()=>s});var r=n(7462),o=(n(7294),n(3905));const i={title:"Setup",sidebar_position:2},a="Frontend setup",l={unversionedId:"frontend/setup",id:"frontend/setup",title:"Setup",description:"How to setup your machine for frontend development",source:"@site/docs/frontend/setup.md",sourceDirName:"frontend",slug:"/frontend/setup",permalink:"/docs/frontend/setup",draft:!1,editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/docs/frontend/setup.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{title:"Setup",sidebar_position:2},sidebar:"frontendSidebar",previous:{title:"RTFM",permalink:"/docs/frontend/rtfm"},next:{title:"Stack",permalink:"/docs/frontend/stack"}},u={},s=[{value:"general topics",id:"general-topics",level:2},{value:"setting up your IDE",id:"setting-up-your-ide",level:2},{value:"debugging with emulators / devices",id:"debugging-with-emulators--devices",level:2},{value:"Install software",id:"install-software",level:2},{value:"Mac",id:"mac",level:3},{value:"Linux",id:"linux",level:3},{value:"Windows",id:"windows",level:3}],p={toc:s};function c(e){let{components:t,...n}=e;return(0,o.kt)("wrapper",(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"frontend-setup"},"Frontend setup"),(0,o.kt)("p",null,"How to setup your machine for frontend development"),(0,o.kt)("h2",{id:"general-topics"},"general topics"),(0,o.kt)("h2",{id:"setting-up-your-ide"},"setting up your IDE"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"vscode",(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"})))),(0,o.kt)("h1",{id:"frontend-specific"},"frontend specific"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"yarn / npm / package.json"),(0,o.kt)("li",{parentName:"ul"},"nvm"),(0,o.kt)("li",{parentName:"ul"},"browser extensions",(0,o.kt)("ul",{parentName:"li"},(0,o.kt)("li",{parentName:"ul"},"preact"),(0,o.kt)("li",{parentName:"ul"},"redux"),(0,o.kt)("li",{parentName:"ul"},"(google analytics)")))),(0,o.kt)("h2",{id:"debugging-with-emulators--devices"},"debugging with emulators / devices"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"xcode"),(0,o.kt)("li",{parentName:"ul"},"android studio")),(0,o.kt)("h2",{id:"install-software"},"Install software"),(0,o.kt)("h3",{id:"mac"},"Mac"),(0,o.kt)("p",null,"First of all install ",(0,o.kt)("inlineCode",{parentName:"p"},"brew")," from ",(0,o.kt)("a",{parentName:"p",href:"https://brew.sh"},"https://brew.sh")),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-zsh"},"brew install nvm\nbrew cask install iterm2\n")),(0,o.kt)("h3",{id:"linux"},"Linux"),(0,o.kt)("h3",{id:"windows"},"Windows"),(0,o.kt)("p",null,"Install Linux ;)"))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0606a637.226328e0.js b/assets/js/0606a637.226328e0.js deleted file mode 100644 index fe229c4..0000000 --- a/assets/js/0606a637.226328e0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[2648],{3905:function(e,t,n){n.d(t,{Zo:function(){return s},kt:function(){return f}});var r=n(7294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var u=r.createContext({}),c=function(e){var t=r.useContext(u),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},s=function(e){var t=c(e.components);return r.createElement(u.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},d=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,u=e.parentName,s=a(e,["components","mdxType","originalType","parentName"]),d=c(n),f=o,m=d["".concat(u,".").concat(f)]||d[f]||p[f]||i;return n?r.createElement(m,l(l({ref:t},s),{},{components:n})):r.createElement(m,l({ref:t},s))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,l=new Array(i);l[0]=d;var a={};for(var u in t)hasOwnProperty.call(t,u)&&(a[u]=t[u]);a.originalType=e,a.mdxType="string"==typeof e?e:o,l[1]=a;for(var c=2;c{o.exports=JSON.parse('{"permalink":"/blog/tags/bundle-size","page":1,"postsPerPage":10,"totalPages":1,"totalCount":1,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/0911540a.3413b503.js b/assets/js/0911540a.3413b503.js new file mode 100644 index 0000000..6655b14 --- /dev/null +++ b/assets/js/0911540a.3413b503.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[7566],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>k});var o=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function i(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var m=o.createContext({}),s=function(e){var t=o.useContext(m),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},u=function(e){var t=s(e.components);return o.createElement(m.Provider,{value:t},e.children)},c={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},p=o.forwardRef((function(e,t){var r=e.components,n=e.mdxType,a=e.originalType,m=e.parentName,u=l(e,["components","mdxType","originalType","parentName"]),p=s(r),k=n,f=p["".concat(m,".").concat(k)]||p[k]||c[k]||a;return r?o.createElement(f,i(i({ref:t},u),{},{components:r})):o.createElement(f,i({ref:t},u))}));function k(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var a=r.length,i=new Array(a);i[0]=p;var l={};for(var m in t)hasOwnProperty.call(t,m)&&(l[m]=t[m]);l.originalType=e,l.mdxType="string"==typeof e?e:n,i[1]=l;for(var s=2;s{r.r(t),r.d(t,{assets:()=>m,contentTitle:()=>i,default:()=>c,frontMatter:()=>a,metadata:()=>l,toc:()=>s});var o=r(7462),n=(r(7294),r(3905));const a={},i="Working remotely",l={unversionedId:"general/work/remote-work",id:"general/work/remote-work",title:"Working remotely",description:"Especially since the beginning of the corona pandemy working remotely has become a very important topic. The foomo team has extensive experience working remotely as we are a distributed team.",source:"@site/docs/general/work/remote-work.md",sourceDirName:"general/work",slug:"/general/work/remote-work",permalink:"/docs/general/work/remote-work",draft:!1,editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/docs/general/work/remote-work.md",tags:[],version:"current",frontMatter:{},sidebar:"generalSidebar",previous:{title:"Working as a software developer",permalink:"/docs/general/work/general"},next:{title:"Package managers",permalink:"/docs/general/package-managers"}},m={},s=[{value:"Requirements",id:"requirements",level:2},{value:"The good parts",id:"the-good-parts",level:2},{value:"The bad parts",id:"the-bad-parts",level:2},{value:"When working from home",id:"when-working-from-home",level:2}],u={toc:s};function c(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,o.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"working-remotely"},"Working remotely"),(0,n.kt)("p",null,"Especially since the beginning of the corona pandemy working remotely has become a very important topic. The foomo team has extensive experience working remotely as we are a distributed team. "),(0,n.kt)("p",null,"Disclaimer: we are almost exclusively working in one timezone."),(0,n.kt)("h2",{id:"requirements"},"Requirements"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"fast and reliable internet connection"),(0,n.kt)("li",{parentName:"ul"},"a proper ",(0,n.kt)("a",{parentName:"li",href:"../setup/workplace"},"workplace"))),(0,n.kt)("h2",{id:"the-good-parts"},"The good parts"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"no commute"),(0,n.kt)("li",{parentName:"ul"},"less distractions from team members")),(0,n.kt)("h2",{id:"the-bad-parts"},"The bad parts"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"harder to separate between work and your private life"),(0,n.kt)("li",{parentName:"ul"},"less informal communication with teammates"),(0,n.kt)("li",{parentName:"ul"},"less human interaction")),(0,n.kt)("h2",{id:"when-working-from-home"},"When working from home"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"make sure, that you have a routine, that still takes you to work",(0,n.kt)("ul",{parentName:"li"},(0,n.kt)("li",{parentName:"ul"},"fixed working hours "),(0,n.kt)("li",{parentName:"ul"},'"go to work" and "leave work" even if you do not leave your home'),(0,n.kt)("li",{parentName:"ul"},"if possible use a room, that is for work only"),(0,n.kt)("li",{parentName:"ul"},"have clear rules for your family / roomates, when at work "))),(0,n.kt)("li",{parentName:"ul"},"when communication with teammates use video calls"),(0,n.kt)("li",{parentName:"ul"},"take time for your teammates and have a virtual coffee break with someone at least once a day"),(0,n.kt)("li",{parentName:"ul"},"invest half the time, that you save by not communiting to exercise")))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0911540a.799fdb1a.js b/assets/js/0911540a.799fdb1a.js deleted file mode 100644 index d4f80cd..0000000 --- a/assets/js/0911540a.799fdb1a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[7566],{3905:function(e,t,r){r.d(t,{Zo:function(){return c},kt:function(){return f}});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var m=n.createContext({}),u=function(e){var t=n.useContext(m),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},c=function(e){var t=u(e.components);return n.createElement(m.Provider,{value:t},e.children)},s={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},p=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,m=e.parentName,c=l(e,["components","mdxType","originalType","parentName"]),p=u(r),f=o,k=p["".concat(m,".").concat(f)]||p[f]||s[f]||a;return r?n.createElement(k,i(i({ref:t},c),{},{components:r})):n.createElement(k,i({ref:t},c))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=p;var l={};for(var m in t)hasOwnProperty.call(t,m)&&(l[m]=t[m]);l.originalType=e,l.mdxType="string"==typeof e?e:o,i[1]=l;for(var u=2;u=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var a=n.createContext({}),s=function(e){var t=n.useContext(a),r=t;return e&&(r="function"==typeof e?e(t):p(p({},t),e)),r},u=function(e){var t=s(e.components);return n.createElement(a.Provider,{value:t},e.children)},f={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},l=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,c=e.originalType,a=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),l=s(r),m=o,g=l["".concat(a,".").concat(m)]||l[m]||f[m]||c;return r?n.createElement(g,p(p({ref:t},u),{},{components:r})):n.createElement(g,p({ref:t},u))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var c=r.length,p=new Array(c);p[0]=l;var i={};for(var a in t)hasOwnProperty.call(t,a)&&(i[a]=t[a]);i.originalType=e,i.mdxType="string"==typeof e?e:o,p[1]=i;for(var s=2;s{r.d(t,{Zo:()=>l,kt:()=>m});var o=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function c(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function p(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var s=o.createContext({}),i=function(e){var t=o.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):p(p({},t),e)),r},l=function(e){var t=i(e.components);return o.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},f=o.forwardRef((function(e,t){var r=e.components,n=e.mdxType,c=e.originalType,s=e.parentName,l=a(e,["components","mdxType","originalType","parentName"]),f=i(r),m=n,g=f["".concat(s,".").concat(m)]||f[m]||u[m]||c;return r?o.createElement(g,p(p({ref:t},l),{},{components:r})):o.createElement(g,p({ref:t},l))}));function m(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var c=r.length,p=new Array(c);p[0]=f;var a={};for(var s in t)hasOwnProperty.call(t,s)&&(a[s]=t[s]);a.originalType=e,a.mdxType="string"==typeof e?e:n,p[1]=a;for(var i=2;i{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>p,default:()=>u,frontMatter:()=>c,metadata:()=>a,toc:()=>i});var o=r(7462),n=(r(7294),r(3905));const c={},p="gotsrpc",a={unversionedId:"projects/gotsrpc",id:"projects/gotsrpc",title:"gotsrpc",description:"https://github.com/foomo/gotsrpc",source:"@site/docs/projects/gotsrpc.md",sourceDirName:"projects",slug:"/projects/gotsrpc",permalink:"/docs/projects/gotsrpc",draft:!1,editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/docs/projects/gotsrpc.md",tags:[],version:"current",frontMatter:{},sidebar:"projectsSidebar",previous:{title:"contentful",permalink:"/docs/projects/cms/contentful"},next:{title:"keel",permalink:"/docs/projects/libraries/keel"}},s={},i=[],l={toc:i};function u(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,o.Z)({},l,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"gotsrpc"},"gotsrpc"),(0,n.kt)("p",null,(0,n.kt)("a",{parentName:"p",href:"https://github.com/foomo/gotsrpc"},"https://github.com/foomo/gotsrpc")),(0,n.kt)("p",null,"Since we are using go when writing"))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/0fc9ee27.c7976dbb.js b/assets/js/0fc9ee27.c7976dbb.js new file mode 100644 index 0000000..1374f0d --- /dev/null +++ b/assets/js/0fc9ee27.c7976dbb.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[1658],{8682:o=>{o.exports=JSON.parse('{"permalink":"/blog/tags/foomo","page":1,"postsPerPage":10,"totalPages":1,"totalCount":1,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/12623ccc.96cd172a.js b/assets/js/12623ccc.96cd172a.js new file mode 100644 index 0000000..c419109 --- /dev/null +++ b/assets/js/12623ccc.96cd172a.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[8423],{3905:(e,t,r)=>{r.d(t,{Zo:()=>s,kt:()=>d});var o=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function i(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var p=o.createContext({}),c=function(e){var t=o.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},s=function(e){var t=c(e.components);return o.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var r=e.components,n=e.mdxType,a=e.originalType,p=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),m=c(r),d=n,f=m["".concat(p,".").concat(d)]||m[d]||u[d]||a;return r?o.createElement(f,i(i({ref:t},s),{},{components:r})):o.createElement(f,i({ref:t},s))}));function d(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var a=r.length,i=new Array(a);i[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:n,i[1]=l;for(var c=2;c{r.d(t,{h:()=>n});var o=r(7294);const n=e=>{let{proportion:t,src:r}=e;return t||(t=4/3),o.createElement("div",{style:{width:"100%",height:0,paddingTop:100/t+"%",position:"relative",float:"left"}},o.createElement("iframe",{style:{width:"100%",height:"100%",display:"block",position:"absolute",top:0,left:0},src:r,frameBorder:"0",scrolling:"no",allowFullScreen:!0}))}},9030:(e,t,r)=>{r.d(t,{k:()=>a});var o=r(7294);const n="undefined"==typeof localStorage,a=e=>{const[t,r]=(0,o.useState)((a=e.id,"undefined"!=typeof localStorage&&localStorage.getItem(a)));var a;return(0,o.useEffect)((()=>{console.log("well it is cool",e.id,{isCool:t,SSR:n})}),[t,n]),t?e.children:o.createElement("div",null,o.createElement("button",{className:"button button--lg button--secondary",onClick:t=>{localStorage.setItem(e.id,"yes"),r(!0)}},e.topic))}},9850:(e,t,r)=>{r.d(t,{O:()=>i});var o=r(7294),n=r(4991),a=r(9030);const i=e=>o.createElement(a.k,{topic:"load miro board from https://miro.com",id:"miroIsCool"},o.createElement(n.h,{src:"https://miro.com/app/embed/"+e.id+"/?pres=1&autoplay=yep",proportion:e.proportion}))},4782:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>m,frontMatter:()=>i,metadata:()=>p,toc:()=>s});var o=r(7462),n=(r(7294),r(3905)),a=r(9850);const i={id:"architecture",sidebar_label:"Architecture",sidebar_position:1},l="Architecture overview",p={unversionedId:"projects/architecture",id:"projects/architecture",title:"Architecture overview",description:"backend services",source:"@site/docs/projects/architecture.mdx",sourceDirName:"projects",slug:"/projects/architecture",permalink:"/docs/projects/architecture",draft:!1,editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/docs/projects/architecture.mdx",tags:[],version:"current",sidebarPosition:1,frontMatter:{id:"architecture",sidebar_label:"Architecture",sidebar_position:1},sidebar:"projectsSidebar",previous:{title:"Intro",permalink:"/docs/projects/intro"},next:{title:"Intro",permalink:"/docs/projects/cms/intro"}},c={},s=[{value:"backend services",id:"backend-services",level:2},{value:"Foomo projects supporting development with Go",id:"foomo-projects-supporting-development-with-go",level:3},{value:"Frontends",id:"frontends",level:2},{value:"Foomo projects supporting development with Next.js",id:"foomo-projects-supporting-development-with-nextjs",level:3}],u={toc:s};function m(e){let{components:t,...r}=e;return(0,n.kt)("wrapper",(0,o.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"architecture-overview"},"Architecture overview"),(0,n.kt)("h2",{id:"backend-services"},"backend services"),(0,n.kt)("p",null,"When it comes to writing backend services we have decided for Go ",(0,n.kt)("a",{parentName:"p",href:"https://go.dev"},"https://go.dev")," - let me list the main reasons why - Go is:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"simple and fun "),(0,n.kt)("li",{parentName:"ul"},"fast when compiling"),(0,n.kt)("li",{parentName:"ul"},"fast at runtime"),(0,n.kt)("li",{parentName:"ul"},"friendly to your machines"),(0,n.kt)("li",{parentName:"ul"},"friendly to you as a programmer"),(0,n.kt)("li",{parentName:"ul"},"equipped with a top notch runtime"),(0,n.kt)("li",{parentName:"ul"},"extremely well balanced in its design as a language, that is highly consumable for humans and machines"),(0,n.kt)("li",{parentName:"ul"},"not about the features it has "),(0,n.kt)("li",{parentName:"ul"},"about what has been left out"),(0,n.kt)("li",{parentName:"ul"},"easy to read"),(0,n.kt)("li",{parentName:"ul"},"sustainable")),(0,n.kt)("h3",{id:"foomo-projects-supporting-development-with-go"},"Foomo projects supporting development with Go"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"libraries/keel"},"keel")," - opinionated way to run services"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"gotsrpc"},"gotsrpc")," - rpc framework / code generator"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"utilities/gograpple"},"gograpple")," - human friendly way to debug go programs running in k8s"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"webgrapple"},"webgrapple")," - a development proxy")),(0,n.kt)("h2",{id:"frontends"},"Frontends"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"TypeScript ",(0,n.kt)("a",{parentName:"li",href:"https://www.typescriptlang.org/"},"https://www.typescriptlang.org/")),(0,n.kt)("li",{parentName:"ul"},"Next.js ",(0,n.kt)("a",{parentName:"li",href:"https://nextjs.org/"},"https://nextjs.org/")),(0,n.kt)("li",{parentName:"ul"},"Styled components ",(0,n.kt)("a",{parentName:"li",href:"https://styled-components.com/"},"https://styled-components.com/")),(0,n.kt)("li",{parentName:"ul"},"Storybook ",(0,n.kt)("a",{parentName:"li",href:"https://storybook.js.org/"},"https://storybook.js.org/"))),(0,n.kt)("h3",{id:"foomo-projects-supporting-development-with-nextjs"},"Foomo projects supporting development with Next.js"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"gotsrpc"},"gotsrpc")," - rpc framework / code generator"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"webgrapple"},"webgrapple")," - a development proxy")),(0,n.kt)(a.O,{id:"o9J_ljlwpFY=",proportion:4/3,mdxType:"Miro"}))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/12623ccc.f1d74277.js b/assets/js/12623ccc.f1d74277.js deleted file mode 100644 index 81b1b8a..0000000 --- a/assets/js/12623ccc.f1d74277.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[8423],{3905:function(e,t,r){r.d(t,{Zo:function(){return s},kt:function(){return d}});var o=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,o)}return r}function a(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var p=o.createContext({}),c=function(e){var t=o.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):a(a({},t),e)),r},s=function(e){var t=c(e.components);return o.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return o.createElement(o.Fragment,{},t)}},m=o.forwardRef((function(e,t){var r=e.components,n=e.mdxType,i=e.originalType,p=e.parentName,s=l(e,["components","mdxType","originalType","parentName"]),m=c(r),d=n,f=m["".concat(p,".").concat(d)]||m[d]||u[d]||i;return r?o.createElement(f,a(a({ref:t},s),{},{components:r})):o.createElement(f,a({ref:t},s))}));function d(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var i=r.length,a=new Array(i);a[0]=m;var l={};for(var p in t)hasOwnProperty.call(t,p)&&(l[p]=t[p]);l.originalType=e,l.mdxType="string"==typeof e?e:n,a[1]=l;for(var c=2;c{o.exports=JSON.parse('{"label":"foomo","permalink":"/blog/tags/foomo","allTagsPath":"/blog/tags","count":1}')}}]); \ No newline at end of file diff --git a/assets/js/15d070c3.7ae335ea.js b/assets/js/15d070c3.7ae335ea.js new file mode 100644 index 0000000..82bce1b --- /dev/null +++ b/assets/js/15d070c3.7ae335ea.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[4963],{8055:o=>{o.exports=JSON.parse('{"permalink":"/blog/tags/backend","page":1,"postsPerPage":10,"totalPages":1,"totalCount":2,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/17896441.13c2a0a7.js b/assets/js/17896441.13c2a0a7.js new file mode 100644 index 0000000..6181f48 --- /dev/null +++ b/assets/js/17896441.13c2a0a7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[7918],{9055:(e,t,n)=>{n.r(t),n.d(t,{default:()=>ue});var a=n(7294),l=n(833),r=n(902);const s=a.createContext(null);function o(e){let{children:t,content:n}=e;const l=function(e){return(0,a.useMemo)((()=>({metadata:e.metadata,frontMatter:e.frontMatter,assets:e.assets,contentTitle:e.contentTitle,toc:e.toc})),[e])}(n);return a.createElement(s.Provider,{value:l},t)}function c(){const e=(0,a.useContext)(s);if(null===e)throw new r.i6("DocProvider");return e}function i(){const{metadata:e,frontMatter:t,assets:n}=c();return a.createElement(l.d,{title:e.title,description:e.description,keywords:t.keywords,image:n.image??t.image})}var d=n(6010),m=n(7524),u=n(7462),v=n(5999),b=n(2244);function h(e){const{previous:t,next:n}=e;return a.createElement("nav",{className:"pagination-nav docusaurus-mt-lg","aria-label":(0,v.I)({id:"theme.docs.paginator.navAriaLabel",message:"Docs pages navigation",description:"The ARIA label for the docs pagination"})},t&&a.createElement(b.Z,(0,u.Z)({},t,{subLabel:a.createElement(v.Z,{id:"theme.docs.paginator.previous",description:"The label used to navigate to the previous doc"},"Previous")})),n&&a.createElement(b.Z,(0,u.Z)({},n,{subLabel:a.createElement(v.Z,{id:"theme.docs.paginator.next",description:"The label used to navigate to the next doc"},"Next"),isNext:!0})))}function p(){const{metadata:e}=c();return a.createElement(h,{previous:e.previous,next:e.next})}var f=n(2263),E=n(9960),g=n(143),L=n(5281),N=n(373),_=n(4477);const Z={unreleased:function(e){let{siteTitle:t,versionMetadata:n}=e;return a.createElement(v.Z,{id:"theme.docs.versions.unreleasedVersionLabel",description:"The label used to tell the user that he's browsing an unreleased doc version",values:{siteTitle:t,versionLabel:a.createElement("b",null,n.label)}},"This is unreleased documentation for {siteTitle} {versionLabel} version.")},unmaintained:function(e){let{siteTitle:t,versionMetadata:n}=e;return a.createElement(v.Z,{id:"theme.docs.versions.unmaintainedVersionLabel",description:"The label used to tell the user that he's browsing an unmaintained doc version",values:{siteTitle:t,versionLabel:a.createElement("b",null,n.label)}},"This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.")}};function k(e){const t=Z[e.versionMetadata.banner];return a.createElement(t,e)}function C(e){let{versionLabel:t,to:n,onClick:l}=e;return a.createElement(v.Z,{id:"theme.docs.versions.latestVersionSuggestionLabel",description:"The label used to tell the user to check the latest version",values:{versionLabel:t,latestVersionLink:a.createElement("b",null,a.createElement(E.Z,{to:n,onClick:l},a.createElement(v.Z,{id:"theme.docs.versions.latestVersionLinkLabel",description:"The label used for the latest version suggestion link label"},"latest version")))}},"For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).")}function x(e){let{className:t,versionMetadata:n}=e;const{siteConfig:{title:l}}=(0,f.Z)(),{pluginId:r}=(0,g.gA)({failfast:!0}),{savePreferredVersionName:s}=(0,N.J)(r),{latestDocSuggestion:o,latestVersionSuggestion:c}=(0,g.Jo)(r),i=o??(m=c).docs.find((e=>e.id===m.mainDocId));var m;return a.createElement("div",{className:(0,d.Z)(t,L.k.docs.docVersionBanner,"alert alert--warning margin-bottom--md"),role:"alert"},a.createElement("div",null,a.createElement(k,{siteTitle:l,versionMetadata:n})),a.createElement("div",{className:"margin-top--md"},a.createElement(C,{versionLabel:c.label,to:i.path,onClick:()=>s(c.name)})))}function T(e){let{className:t}=e;const n=(0,_.E)();return n.banner?a.createElement(x,{className:t,versionMetadata:n}):null}function H(e){let{className:t}=e;const n=(0,_.E)();return n.badge?a.createElement("span",{className:(0,d.Z)(t,L.k.docs.docVersionBadge,"badge badge--secondary")},a.createElement(v.Z,{id:"theme.docs.versionBadge.label",values:{versionLabel:n.label}},"Version: {versionLabel}")):null}function U(e){let{lastUpdatedAt:t,formattedLastUpdatedAt:n}=e;return a.createElement(v.Z,{id:"theme.lastUpdated.atDate",description:"The words used to describe on which date a page has been last updated",values:{date:a.createElement("b",null,a.createElement("time",{dateTime:new Date(1e3*t).toISOString()},n))}}," on {date}")}function w(e){let{lastUpdatedBy:t}=e;return a.createElement(v.Z,{id:"theme.lastUpdated.byUser",description:"The words used to describe by who the page has been last updated",values:{user:a.createElement("b",null,t)}}," by {user}")}function y(e){let{lastUpdatedAt:t,formattedLastUpdatedAt:n,lastUpdatedBy:l}=e;return a.createElement("span",{className:L.k.common.lastUpdated},a.createElement(v.Z,{id:"theme.lastUpdated.lastUpdatedAtBy",description:"The sentence used to display when a page has been last updated, and by who",values:{atDate:t&&n?a.createElement(U,{lastUpdatedAt:t,formattedLastUpdatedAt:n}):"",byUser:l?a.createElement(w,{lastUpdatedBy:l}):""}},"Last updated{atDate}{byUser}"),!1)}var A=n(4881),M=n(1526);const I="lastUpdated_vwxv";function B(e){return a.createElement("div",{className:(0,d.Z)(L.k.docs.docFooterTagsRow,"row margin-bottom--sm")},a.createElement("div",{className:"col"},a.createElement(M.Z,e)))}function V(e){let{editUrl:t,lastUpdatedAt:n,lastUpdatedBy:l,formattedLastUpdatedAt:r}=e;return a.createElement("div",{className:(0,d.Z)(L.k.docs.docFooterEditMetaRow,"row")},a.createElement("div",{className:"col"},t&&a.createElement(A.Z,{editUrl:t})),a.createElement("div",{className:(0,d.Z)("col",I)},(n||l)&&a.createElement(y,{lastUpdatedAt:n,formattedLastUpdatedAt:r,lastUpdatedBy:l})))}function O(){const{metadata:e}=c(),{editUrl:t,lastUpdatedAt:n,formattedLastUpdatedAt:l,lastUpdatedBy:r,tags:s}=e,o=s.length>0,i=!!(t||n||r);return o||i?a.createElement("footer",{className:(0,d.Z)(L.k.docs.docFooter,"docusaurus-mt-lg")},o&&a.createElement(B,{tags:s}),i&&a.createElement(V,{editUrl:t,lastUpdatedAt:n,lastUpdatedBy:r,formattedLastUpdatedAt:l})):null}var P=n(6043),S=n(3743);const D="tocCollapsibleButton_TO0P",R="tocCollapsibleButtonExpanded_MG3E";function z(e){let{collapsed:t,...n}=e;return a.createElement("button",(0,u.Z)({type:"button"},n,{className:(0,d.Z)("clean-btn",D,!t&&R,n.className)}),a.createElement(v.Z,{id:"theme.TOCCollapsible.toggleButtonLabel",description:"The label used by the button on the collapsible TOC component"},"On this page"))}const F="tocCollapsible_ETCw",q="tocCollapsibleContent_vkbj",j="tocCollapsibleExpanded_sAul";function G(e){let{toc:t,className:n,minHeadingLevel:l,maxHeadingLevel:r}=e;const{collapsed:s,toggleCollapsed:o}=(0,P.u)({initialState:!0});return a.createElement("div",{className:(0,d.Z)(F,!s&&j,n)},a.createElement(z,{collapsed:s,onClick:o}),a.createElement(P.z,{lazy:!0,className:q,collapsed:s},a.createElement(S.Z,{toc:t,minHeadingLevel:l,maxHeadingLevel:r})))}const $="tocMobile_ITEo";function J(){const{toc:e,frontMatter:t}=c();return a.createElement(G,{toc:e,minHeadingLevel:t.toc_min_heading_level,maxHeadingLevel:t.toc_max_heading_level,className:(0,d.Z)(L.k.docs.docTocMobile,$)})}var Q=n(9407);function W(){const{toc:e,frontMatter:t}=c();return a.createElement(Q.Z,{toc:e,minHeadingLevel:t.toc_min_heading_level,maxHeadingLevel:t.toc_max_heading_level,className:L.k.docs.docTocDesktop})}var X=n(2503),K=n(5203);function Y(e){let{children:t}=e;const n=function(){const{metadata:e,frontMatter:t,contentTitle:n}=c();return t.hide_title||void 0!==n?null:e.title}();return a.createElement("div",{className:(0,d.Z)(L.k.docs.docMarkdown,"markdown")},n&&a.createElement("header",null,a.createElement(X.Z,{as:"h1"},n)),a.createElement(K.Z,null,t))}var ee=n(2802),te=n(8596),ne=n(4996);function ae(e){return a.createElement("svg",(0,u.Z)({viewBox:"0 0 24 24"},e),a.createElement("path",{d:"M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z",fill:"currentColor"}))}const le={breadcrumbsContainer:"breadcrumbsContainer_Z_bl",breadcrumbHomeIcon:"breadcrumbHomeIcon_OVgt"};function re(e){let{children:t,href:n,isLast:l}=e;const r="breadcrumbs__link";return l?a.createElement("span",{className:r,itemProp:"name"},t):n?a.createElement(E.Z,{className:r,href:n,itemProp:"item"},a.createElement("span",{itemProp:"name"},t)):a.createElement("span",{className:r},t)}function se(e){let{children:t,active:n,index:l,addMicrodata:r}=e;return a.createElement("li",(0,u.Z)({},r&&{itemScope:!0,itemProp:"itemListElement",itemType:"https://schema.org/ListItem"},{className:(0,d.Z)("breadcrumbs__item",{"breadcrumbs__item--active":n})}),t,a.createElement("meta",{itemProp:"position",content:String(l+1)}))}function oe(){const e=(0,ne.Z)("/");return a.createElement("li",{className:"breadcrumbs__item"},a.createElement(E.Z,{"aria-label":(0,v.I)({id:"theme.docs.breadcrumbs.home",message:"Home page",description:"The ARIA label for the home page in the breadcrumbs"}),className:(0,d.Z)("breadcrumbs__link",le.breadcrumbsItemLink),href:e},a.createElement(ae,{className:le.breadcrumbHomeIcon})))}function ce(){const e=(0,ee.s1)(),t=(0,te.Ns)();return e?a.createElement("nav",{className:(0,d.Z)(L.k.docs.docBreadcrumbs,le.breadcrumbsContainer),"aria-label":(0,v.I)({id:"theme.docs.breadcrumbs.navAriaLabel",message:"Breadcrumbs",description:"The ARIA label for the breadcrumbs"})},a.createElement("ul",{className:"breadcrumbs",itemScope:!0,itemType:"https://schema.org/BreadcrumbList"},t&&a.createElement(oe,null),e.map(((t,n)=>{const l=n===e.length-1;return a.createElement(se,{key:n,active:l,index:n,addMicrodata:!!t.href},a.createElement(re,{href:t.href,isLast:l},t.label))})))):null}const ie="docItemContainer_Djhp",de="docItemCol_VOVn";function me(e){let{children:t}=e;const n=function(){const{frontMatter:e,toc:t}=c(),n=(0,m.i)(),l=e.hide_table_of_contents,r=!l&&t.length>0;return{hidden:l,mobile:r?a.createElement(J,null):void 0,desktop:!r||"desktop"!==n&&"ssr"!==n?void 0:a.createElement(W,null)}}();return a.createElement("div",{className:"row"},a.createElement("div",{className:(0,d.Z)("col",!n.hidden&&de)},a.createElement(T,null),a.createElement("div",{className:ie},a.createElement("article",null,a.createElement(ce,null),a.createElement(H,null),n.mobile,a.createElement(Y,null,t),a.createElement(O,null)),a.createElement(p,null))),n.desktop&&a.createElement("div",{className:"col col--3"},n.desktop))}function ue(e){const t=`docs-doc-id-${e.content.metadata.unversionedId}`,n=e.content;return a.createElement(o,{content:e.content},a.createElement(l.FG,{className:t},a.createElement(i,null),a.createElement(me,null,a.createElement(n,null))))}},4881:(e,t,n)=>{n.d(t,{Z:()=>d});var a=n(7294),l=n(5999),r=n(5281),s=n(7462),o=n(6010);const c="iconEdit_Z9Sw";function i(e){let{className:t,...n}=e;return a.createElement("svg",(0,s.Z)({fill:"currentColor",height:"20",width:"20",viewBox:"0 0 40 40",className:(0,o.Z)(c,t),"aria-hidden":"true"},n),a.createElement("g",null,a.createElement("path",{d:"m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"})))}function d(e){let{editUrl:t}=e;return a.createElement("a",{href:t,target:"_blank",rel:"noreferrer noopener",className:r.k.common.editThisPage},a.createElement(i,null),a.createElement(l.Z,{id:"theme.common.editThisPage",description:"The link label to edit the current page"},"Edit this page"))}},2244:(e,t,n)=>{n.d(t,{Z:()=>s});var a=n(7294),l=n(6010),r=n(9960);function s(e){const{permalink:t,title:n,subLabel:s,isNext:o}=e;return a.createElement(r.Z,{className:(0,l.Z)("pagination-nav__link",o?"pagination-nav__link--next":"pagination-nav__link--prev"),to:t},s&&a.createElement("div",{className:"pagination-nav__sublabel"},s),a.createElement("div",{className:"pagination-nav__label"},n))}},9407:(e,t,n)=>{n.d(t,{Z:()=>c});var a=n(7462),l=n(7294),r=n(6010),s=n(3743);const o="tableOfContents_bqdL";function c(e){let{className:t,...n}=e;return l.createElement("div",{className:(0,r.Z)(o,"thin-scrollbar",t)},l.createElement(s.Z,(0,a.Z)({},n,{linkClassName:"table-of-contents__link toc-highlight",linkActiveClassName:"table-of-contents__link--active"})))}},3743:(e,t,n)=>{n.d(t,{Z:()=>b});var a=n(7462),l=n(7294),r=n(6668);function s(e){const t=e.map((e=>({...e,parentIndex:-1,children:[]}))),n=Array(7).fill(-1);t.forEach(((e,t)=>{const a=n.slice(2,e.level);e.parentIndex=Math.max(...a),n[e.level]=t}));const a=[];return t.forEach((e=>{const{parentIndex:n,...l}=e;n>=0?t[n].children.push(l):a.push(l)})),a}function o(e){let{toc:t,minHeadingLevel:n,maxHeadingLevel:a}=e;return t.flatMap((e=>{const t=o({toc:e.children,minHeadingLevel:n,maxHeadingLevel:a});return function(e){return e.level>=n&&e.level<=a}(e)?[{...e,children:t}]:t}))}function c(e){const t=e.getBoundingClientRect();return t.top===t.bottom?c(e.parentNode):t}function i(e,t){let{anchorTopOffset:n}=t;const a=e.find((e=>c(e).top>=n));if(a){return function(e){return e.top>0&&e.bottom{e.current=t?0:document.querySelector(".navbar").clientHeight}),[t]),e}function m(e){const t=(0,l.useRef)(void 0),n=d();(0,l.useEffect)((()=>{if(!e)return()=>{};const{linkClassName:a,linkActiveClassName:l,minHeadingLevel:r,maxHeadingLevel:s}=e;function o(){const e=function(e){return Array.from(document.getElementsByClassName(e))}(a),o=function(e){let{minHeadingLevel:t,maxHeadingLevel:n}=e;const a=[];for(let l=t;l<=n;l+=1)a.push(`h${l}.anchor`);return Array.from(document.querySelectorAll(a.join()))}({minHeadingLevel:r,maxHeadingLevel:s}),c=i(o,{anchorTopOffset:n.current}),d=e.find((e=>c&&c.id===function(e){return decodeURIComponent(e.href.substring(e.href.indexOf("#")+1))}(e)));e.forEach((e=>{!function(e,n){n?(t.current&&t.current!==e&&t.current.classList.remove(l),e.classList.add(l),t.current=e):e.classList.remove(l)}(e,e===d)}))}return document.addEventListener("scroll",o),document.addEventListener("resize",o),o(),()=>{document.removeEventListener("scroll",o),document.removeEventListener("resize",o)}}),[e,n])}function u(e){let{toc:t,className:n,linkClassName:a,isChild:r}=e;return t.length?l.createElement("ul",{className:r?void 0:n},t.map((e=>l.createElement("li",{key:e.id},l.createElement("a",{href:`#${e.id}`,className:a??void 0,dangerouslySetInnerHTML:{__html:e.value}}),l.createElement(u,{isChild:!0,toc:e.children,className:n,linkClassName:a}))))):null}const v=l.memo(u);function b(e){let{toc:t,className:n="table-of-contents table-of-contents__left-border",linkClassName:c="table-of-contents__link",linkActiveClassName:i,minHeadingLevel:d,maxHeadingLevel:u,...b}=e;const h=(0,r.L)(),p=d??h.tableOfContents.minHeadingLevel,f=u??h.tableOfContents.maxHeadingLevel,E=function(e){let{toc:t,minHeadingLevel:n,maxHeadingLevel:a}=e;return(0,l.useMemo)((()=>o({toc:s(t),minHeadingLevel:n,maxHeadingLevel:a})),[t,n,a])}({toc:t,minHeadingLevel:p,maxHeadingLevel:f});return m((0,l.useMemo)((()=>{if(c&&i)return{linkClassName:c,linkActiveClassName:i,minHeadingLevel:p,maxHeadingLevel:f}}),[c,i,p,f])),l.createElement(v,(0,a.Z)({toc:E,className:n,linkClassName:c},b))}},3008:(e,t,n)=>{n.d(t,{Z:()=>i});var a=n(7294),l=n(6010),r=n(9960);const s="tag_zVej",o="tagRegular_sFm0",c="tagWithCount_h2kH";function i(e){let{permalink:t,label:n,count:i}=e;return a.createElement(r.Z,{href:t,className:(0,l.Z)(s,i?c:o)},n,i&&a.createElement("span",null,i))}},1526:(e,t,n)=>{n.d(t,{Z:()=>i});var a=n(7294),l=n(6010),r=n(5999),s=n(3008);const o="tags_jXut",c="tag_QGVx";function i(e){let{tags:t}=e;return a.createElement(a.Fragment,null,a.createElement("b",null,a.createElement(r.Z,{id:"theme.tags.tagsListLabel",description:"The label alongside a tag list"},"Tags:")),a.createElement("ul",{className:(0,l.Z)(o,"padding--none","margin-left--sm")},t.map((e=>{let{label:t,permalink:n}=e;return a.createElement("li",{key:n,className:c},a.createElement(s.Z,{label:t,permalink:n}))}))))}},4477:(e,t,n)=>{n.d(t,{E:()=>o,q:()=>s});var a=n(7294),l=n(902);const r=a.createContext(null);function s(e){let{children:t,version:n}=e;return a.createElement(r.Provider,{value:n},t)}function o(){const e=(0,a.useContext)(r);if(null===e)throw new l.i6("DocsVersionProvider");return e}}}]); \ No newline at end of file diff --git a/assets/js/17896441.648b34d3.js b/assets/js/17896441.648b34d3.js deleted file mode 100644 index fd30754..0000000 --- a/assets/js/17896441.648b34d3.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[7918],{9483:function(e,t,a){a.r(t),a.d(t,{default:function(){return I}});var n=a(7294),l=a(6010),i=a(3783),r=a(5999),s=a(9960);var o=function(e){var t=e.navLink,a=e.next;return n.createElement(s.Z,{className:(0,l.Z)("pagination-nav__link"),to:t.permalink},n.createElement("div",{className:"pagination-nav__sublabel"},a?n.createElement(r.Z,{id:"theme.docs.paginator.next",description:"The label used to navigate to the next doc"},"Next"):n.createElement(r.Z,{id:"theme.docs.paginator.previous",description:"The label used to navigate to the previous doc"},"Previous")),n.createElement("div",{className:"pagination-nav__label"},t.title))};var c=function(e){var t=e.previous,a=e.next;return n.createElement("nav",{className:"pagination-nav docusaurus-mt-lg","aria-label":(0,r.I)({id:"theme.docs.paginator.navAriaLabel",message:"Docs pages navigation",description:"The ARIA label for the docs pagination"})},n.createElement("div",{className:"pagination-nav__item"},t&&n.createElement(o,{navLink:t})),n.createElement("div",{className:"pagination-nav__item pagination-nav__item--next"},a&&n.createElement(o,{navLink:a,next:!0})))},d=a(2263),m=a(907),u=a(6681);var v={unreleased:function(e){var t=e.siteTitle,a=e.versionMetadata;return n.createElement(r.Z,{id:"theme.docs.versions.unreleasedVersionLabel",description:"The label used to tell the user that he's browsing an unreleased doc version",values:{siteTitle:t,versionLabel:n.createElement("b",null,a.label)}},"This is unreleased documentation for {siteTitle} {versionLabel} version.")},unmaintained:function(e){var t=e.siteTitle,a=e.versionMetadata;return n.createElement(r.Z,{id:"theme.docs.versions.unmaintainedVersionLabel",description:"The label used to tell the user that he's browsing an unmaintained doc version",values:{siteTitle:t,versionLabel:n.createElement("b",null,a.label)}},"This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.")}};function g(e){var t=v[e.versionMetadata.banner];return n.createElement(t,e)}function h(e){var t=e.versionLabel,a=e.to,l=e.onClick;return n.createElement(r.Z,{id:"theme.docs.versions.latestVersionSuggestionLabel",description:"The label used to tell the user to check the latest version",values:{versionLabel:t,latestVersionLink:n.createElement("b",null,n.createElement(s.Z,{to:a,onClick:l},n.createElement(r.Z,{id:"theme.docs.versions.latestVersionLinkLabel",description:"The label used for the latest version suggestion link label"},"latest version")))}},"For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).")}function p(e){var t,a=e.className,i=e.versionMetadata,r=(0,d.Z)().siteConfig.title,s=(0,m.gA)({failfast:!0}).pluginId,o=(0,u.J)(s).savePreferredVersionName,c=(0,m.Jo)(s),v=c.latestDocSuggestion,p=c.latestVersionSuggestion,E=null!=v?v:(t=p).docs.find((function(e){return e.id===t.mainDocId}));return n.createElement("div",{className:(0,l.Z)(a,u.kM.docs.docVersionBanner,"alert alert--warning margin-bottom--md"),role:"alert"},n.createElement("div",null,n.createElement(g,{siteTitle:r,versionMetadata:i})),n.createElement("div",{className:"margin-top--md"},n.createElement(h,{versionLabel:p.label,to:E.path,onClick:function(){return o(p.name)}})))}function E(e){var t=e.className,a=(0,u.E6)();return a.banner?n.createElement(p,{className:t,versionMetadata:a}):null}function b(e){var t=e.className,a=(0,u.E6)();return a.badge?n.createElement("span",{className:(0,l.Z)(t,u.kM.docs.docVersionBadge,"badge badge--secondary")},"Version: ",a.label):null}var f=a(1217);function N(e){var t=e.lastUpdatedAt,a=e.formattedLastUpdatedAt;return n.createElement(r.Z,{id:"theme.lastUpdated.atDate",description:"The words used to describe on which date a page has been last updated",values:{date:n.createElement("b",null,n.createElement("time",{dateTime:new Date(1e3*t).toISOString()},a))}}," on {date}")}function k(e){var t=e.lastUpdatedBy;return n.createElement(r.Z,{id:"theme.lastUpdated.byUser",description:"The words used to describe by who the page has been last updated",values:{user:n.createElement("b",null,t)}}," by {user}")}function Z(e){var t=e.lastUpdatedAt,a=e.formattedLastUpdatedAt,l=e.lastUpdatedBy;return n.createElement("span",{className:u.kM.common.lastUpdated},n.createElement(r.Z,{id:"theme.lastUpdated.lastUpdatedAtBy",description:"The sentence used to display when a page has been last updated, and by who",values:{atDate:t&&a?n.createElement(N,{lastUpdatedAt:t,formattedLastUpdatedAt:a}):"",byUser:l?n.createElement(k,{lastUpdatedBy:l}):""}},"Last updated{atDate}{byUser}"),!1)}var L=a(6753),_=a(62),U="lastUpdated_mt2f";function C(e){return n.createElement("div",{className:(0,l.Z)(u.kM.docs.docFooterTagsRow,"row margin-bottom--sm")},n.createElement("div",{className:"col"},n.createElement(_.Z,e)))}function T(e){var t=e.editUrl,a=e.lastUpdatedAt,i=e.lastUpdatedBy,r=e.formattedLastUpdatedAt;return n.createElement("div",{className:(0,l.Z)(u.kM.docs.docFooterEditMetaRow,"row")},n.createElement("div",{className:"col"},t&&n.createElement(L.Z,{editUrl:t})),n.createElement("div",{className:(0,l.Z)("col",U)},(a||i)&&n.createElement(Z,{lastUpdatedAt:a,formattedLastUpdatedAt:r,lastUpdatedBy:i})))}function y(e){var t=e.content.metadata,a=t.editUrl,i=t.lastUpdatedAt,r=t.formattedLastUpdatedAt,s=t.lastUpdatedBy,o=t.tags,c=o.length>0,d=!!(a||i||s);return c||d?n.createElement("footer",{className:(0,l.Z)(u.kM.docs.docFooter,"docusaurus-mt-lg")},c&&n.createElement(C,{tags:o}),d&&n.createElement(T,{editUrl:a,lastUpdatedAt:i,lastUpdatedBy:s,formattedLastUpdatedAt:r})):null}var w=a(1575),A="tocCollapsible_aw-L",M="tocCollapsibleButton_zr6a",x="tocCollapsibleContent_0dom",H="tocCollapsibleExpanded_FSiv",S=a(5002);function B(e){var t,a=e.toc,i=e.className,s=e.minHeadingLevel,o=e.maxHeadingLevel,c=(0,u.uR)({initialState:!0}),d=c.collapsed,m=c.toggleCollapsed;return n.createElement("div",{className:(0,l.Z)(A,(t={},t[H]=!d,t),i)},n.createElement("button",{type:"button",className:(0,l.Z)("clean-btn",M),onClick:m},n.createElement(r.Z,{id:"theme.TOCCollapsible.toggleButtonLabel",description:"The label used by the button on the collapsible TOC component"},"On this page")),n.createElement(u.zF,{lazy:!0,className:x,collapsed:d},n.createElement(S.Z,{toc:a,minHeadingLevel:s,maxHeadingLevel:o})))}var V=a(9649),F="docItemContainer_oiyr",D="docItemCol_zHA2",O="tocMobile_Tx6Y";function I(e){var t,a=e.content,r=a.metadata,s=a.frontMatter,o=s.image,d=s.keywords,m=s.hide_title,v=s.hide_table_of_contents,g=s.toc_min_heading_level,h=s.toc_max_heading_level,p=r.description,N=r.title,k=!m&&void 0===a.contentTitle,Z=(0,i.Z)(),L=!v&&a.toc&&a.toc.length>0,_=L&&("desktop"===Z||"ssr"===Z);return n.createElement(n.Fragment,null,n.createElement(f.Z,{title:N,description:p,keywords:d,image:o}),n.createElement("div",{className:"row"},n.createElement("div",{className:(0,l.Z)("col",(t={},t[D]=!v,t))},n.createElement(E,null),n.createElement("div",{className:F},n.createElement("article",null,n.createElement(b,null),L&&n.createElement(B,{toc:a.toc,minHeadingLevel:g,maxHeadingLevel:h,className:(0,l.Z)(u.kM.docs.docTocMobile,O)}),n.createElement("div",{className:(0,l.Z)(u.kM.docs.docMarkdown,"markdown")},k&&n.createElement(V.N,null,N),n.createElement(a,null)),n.createElement(y,e)),n.createElement(c,{previous:r.previous,next:r.next}))),_&&n.createElement("div",{className:"col col--3"},n.createElement(w.Z,{toc:a.toc,minHeadingLevel:g,maxHeadingLevel:h,className:u.kM.docs.docTocDesktop}))))}},6753:function(e,t,a){a.d(t,{Z:function(){return u}});var n=a(7294),l=a(5999),i=a(3117),r=a(102),s=a(6010),o="iconEdit_mS5F",c=["className"];var d=function(e){var t=e.className,a=(0,r.Z)(e,c);return n.createElement("svg",(0,i.Z)({fill:"currentColor",height:"20",width:"20",viewBox:"0 0 40 40",className:(0,s.Z)(o,t),"aria-hidden":"true"},a),n.createElement("g",null,n.createElement("path",{d:"m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"})))},m=a(6681);function u(e){var t=e.editUrl;return n.createElement("a",{href:t,target:"_blank",rel:"noreferrer noopener",className:m.kM.common.editThisPage},n.createElement(d,null),n.createElement(l.Z,{id:"theme.common.editThisPage",description:"The link label to edit the current page"},"Edit this page"))}},9649:function(e,t,a){a.d(t,{N:function(){return u},Z:function(){return v}});var n=a(102),l=a(3117),i=a(7294),r=a(6010),s=a(5999),o=a(6681),c="anchorWithStickyNavbar_y2LR",d="anchorWithHideOnScrollNavbar_3ly5",m=["id"],u=function(e){var t=Object.assign({},e);return i.createElement("header",null,i.createElement("h1",(0,l.Z)({},t,{id:void 0}),t.children))},v=function(e){return"h1"===e?u:(t=e,function(e){var a,u=e.id,v=(0,n.Z)(e,m),g=(0,o.LU)().navbar.hideOnScroll;return u?i.createElement(t,(0,l.Z)({},v,{className:(0,r.Z)("anchor",(a={},a[d]=g,a[c]=!g,a)),id:u}),v.children,i.createElement("a",{className:"hash-link",href:"#"+u,title:(0,s.I)({id:"theme.common.headingLinkTitle",message:"Direct link to heading",description:"Title for link to heading"})},"\u200b")):i.createElement(t,v)});var t}},1575:function(e,t,a){a.d(t,{Z:function(){return d}});var n=a(3117),l=a(102),i=a(7294),r=a(6010),s=a(5002),o="tableOfContents_vrFS",c=["className"];var d=function(e){var t=e.className,a=(0,l.Z)(e,c);return i.createElement("div",{className:(0,r.Z)(o,"thin-scrollbar",t)},i.createElement(s.Z,(0,n.Z)({},a,{linkClassName:"table-of-contents__link toc-highlight",linkActiveClassName:"table-of-contents__link--active"})))}},5002:function(e,t,a){a.d(t,{Z:function(){return c}});var n=a(3117),l=a(102),i=a(7294),r=a(6681),s=["toc","className","linkClassName","linkActiveClassName","minHeadingLevel","maxHeadingLevel"];function o(e){var t=e.toc,a=e.className,n=e.linkClassName,l=e.isChild;return t.length?i.createElement("ul",{className:l?void 0:a},t.map((function(e){return i.createElement("li",{key:e.id},i.createElement("a",{href:"#"+e.id,className:null!=n?n:void 0,dangerouslySetInnerHTML:{__html:e.value}}),i.createElement(o,{isChild:!0,toc:e.children,className:a,linkClassName:n}))}))):null}function c(e){var t=e.toc,a=e.className,c=void 0===a?"table-of-contents table-of-contents__left-border":a,d=e.linkClassName,m=void 0===d?"table-of-contents__link":d,u=e.linkActiveClassName,v=void 0===u?void 0:u,g=e.minHeadingLevel,h=e.maxHeadingLevel,p=(0,l.Z)(e,s),E=(0,r.LU)(),b=null!=g?g:E.tableOfContents.minHeadingLevel,f=null!=h?h:E.tableOfContents.maxHeadingLevel,N=(0,r.DA)({toc:t,minHeadingLevel:b,maxHeadingLevel:f}),k=(0,i.useMemo)((function(){if(m&&v)return{linkClassName:m,linkActiveClassName:v,minHeadingLevel:b,maxHeadingLevel:f}}),[m,v,b,f]);return(0,r.Si)(k),i.createElement(o,(0,n.Z)({toc:N,className:c,linkClassName:m},p))}},7774:function(e,t,a){a.d(t,{Z:function(){return c}});var n=a(7294),l=a(6010),i=a(9960),r="tag_WK-t",s="tagRegular_LXbV",o="tagWithCount_S5Zl";var c=function(e){var t,a=e.permalink,c=e.name,d=e.count;return n.createElement(i.Z,{href:a,className:(0,l.Z)(r,(t={},t[s]=!d,t[o]=d,t))},c,d&&n.createElement("span",null,d))}},62:function(e,t,a){a.d(t,{Z:function(){return c}});var n=a(7294),l=a(6010),i=a(5999),r=a(7774),s="tags_NBRY",o="tag_F03v";function c(e){var t=e.tags;return n.createElement(n.Fragment,null,n.createElement("b",null,n.createElement(i.Z,{id:"theme.tags.tagsListLabel",description:"The label alongside a tag list"},"Tags:")),n.createElement("ul",{className:(0,l.Z)(s,"padding--none","margin-left--sm")},t.map((function(e){var t=e.label,a=e.permalink;return n.createElement("li",{key:a,className:o},n.createElement(r.Z,{name:t,permalink:a}))}))))}}}]); \ No newline at end of file diff --git a/assets/js/1a4e3797.66dfb1ef.js b/assets/js/1a4e3797.66dfb1ef.js new file mode 100644 index 0000000..aea7e47 --- /dev/null +++ b/assets/js/1a4e3797.66dfb1ef.js @@ -0,0 +1,2 @@ +/*! For license information please see 1a4e3797.66dfb1ef.js.LICENSE.txt */ +(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[7920],{7331:e=>{function t(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function r(e){return"function"==typeof e}function n(e){return"object"==typeof e&&null!==e}function i(e){return void 0===e}e.exports=t,t.prototype._events=void 0,t.prototype._maxListeners=void 0,t.defaultMaxListeners=10,t.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},t.prototype.emit=function(e){var t,a,s,c,u,o;if(this._events||(this._events={}),"error"===e&&(!this._events.error||n(this._events.error)&&!this._events.error.length)){if((t=arguments[1])instanceof Error)throw t;var h=new Error('Uncaught, unspecified "error" event. ('+t+")");throw h.context=t,h}if(i(a=this._events[e]))return!1;if(r(a))switch(arguments.length){case 1:a.call(this);break;case 2:a.call(this,arguments[1]);break;case 3:a.call(this,arguments[1],arguments[2]);break;default:c=Array.prototype.slice.call(arguments,1),a.apply(this,c)}else if(n(a))for(c=Array.prototype.slice.call(arguments,1),s=(o=a.slice()).length,u=0;u0&&this._events[e].length>s&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},t.prototype.on=t.prototype.addListener,t.prototype.once=function(e,t){if(!r(t))throw TypeError("listener must be a function");var n=!1;function i(){this.removeListener(e,i),n||(n=!0,t.apply(this,arguments))}return i.listener=t,this.on(e,i),this},t.prototype.removeListener=function(e,t){var i,a,s,c;if(!r(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(s=(i=this._events[e]).length,a=-1,i===t||r(i.listener)&&i.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(n(i)){for(c=s;c-- >0;)if(i[c]===t||i[c].listener&&i[c].listener===t){a=c;break}if(a<0)return this;1===i.length?(i.length=0,delete this._events[e]):i.splice(a,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},t.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(r(n=this._events[e]))this.removeListener(e,n);else if(n)for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},t.prototype.listeners=function(e){return this._events&&this._events[e]?r(this._events[e])?[this._events[e]]:this._events[e].slice():[]},t.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(r(t))return 1;if(t)return t.length}return 0},t.listenerCount=function(e,t){return e.listenerCount(t)}},8131:(e,t,r)=>{"use strict";var n=r(9374),i=r(7775),a=r(3076);function s(e,t,r){return new n(e,t,r)}s.version=r(4336),s.AlgoliaSearchHelper=n,s.SearchParameters=i,s.SearchResults=a,e.exports=s},8078:(e,t,r)=>{"use strict";var n=r(7331);function i(e,t){this.main=e,this.fn=t,this.lastResults=null}r(4853)(i,n),i.prototype.detach=function(){this.removeAllListeners(),this.main.detachDerivedHelper(this)},i.prototype.getModifiedState=function(e){return this.fn(e)},e.exports=i},2437:(e,t,r)=>{"use strict";var n=r(2344),i=r(9803),a=r(116),s={addRefinement:function(e,t,r){if(s.isRefined(e,t,r))return e;var i=""+r,a=e[t]?e[t].concat(i):[i],c={};return c[t]=a,n({},c,e)},removeRefinement:function(e,t,r){if(void 0===r)return s.clearRefinement(e,(function(e,r){return t===r}));var n=""+r;return s.clearRefinement(e,(function(e,r){return t===r&&n===e}))},toggleRefinement:function(e,t,r){if(void 0===r)throw new Error("toggleRefinement should be used with a value");return s.isRefined(e,t,r)?s.removeRefinement(e,t,r):s.addRefinement(e,t,r)},clearRefinement:function(e,t,r){if(void 0===t)return a(e)?{}:e;if("string"==typeof t)return i(e,[t]);if("function"==typeof t){var n=!1,s=Object.keys(e).reduce((function(i,a){var s=e[a]||[],c=s.filter((function(e){return!t(e,a,r)}));return c.length!==s.length&&(n=!0),i[a]=c,i}),{});return n?s:e}},isRefined:function(e,t,r){var n=!!e[t]&&e[t].length>0;if(void 0===r||!n)return n;var i=""+r;return-1!==e[t].indexOf(i)}};e.exports=s},7775:(e,t,r)=>{"use strict";var n=r(185),i=r(2344),a=r(2686),s=r(7888),c=r(8023),u=r(9803),o=r(116),h=r(6801),f=r(2437);function l(e,t){return Array.isArray(e)&&Array.isArray(t)?e.length===t.length&&e.every((function(e,r){return l(t[r],e)})):e===t}function m(e){var t=e?m._parseNumbers(e):{};void 0===t.userToken||h(t.userToken)||console.warn("[algoliasearch-helper] The `userToken` parameter is invalid. This can lead to wrong analytics.\n - Format: [a-zA-Z0-9_-]{1,64}"),this.facets=t.facets||[],this.disjunctiveFacets=t.disjunctiveFacets||[],this.hierarchicalFacets=t.hierarchicalFacets||[],this.facetsRefinements=t.facetsRefinements||{},this.facetsExcludes=t.facetsExcludes||{},this.disjunctiveFacetsRefinements=t.disjunctiveFacetsRefinements||{},this.numericRefinements=t.numericRefinements||{},this.tagRefinements=t.tagRefinements||[],this.hierarchicalFacetsRefinements=t.hierarchicalFacetsRefinements||{};var r=this;Object.keys(t).forEach((function(e){var n=-1!==m.PARAMETERS.indexOf(e),i=void 0!==t[e];!n&&i&&(r[e]=t[e])}))}m.PARAMETERS=Object.keys(new m),m._parseNumbers=function(e){if(e instanceof m)return e;var t={};if(["aroundPrecision","aroundRadius","getRankingInfo","minWordSizefor2Typos","minWordSizefor1Typo","page","maxValuesPerFacet","distinct","minimumAroundRadius","hitsPerPage","minProximity"].forEach((function(r){var n=e[r];if("string"==typeof n){var i=parseFloat(n);t[r]=isNaN(i)?n:i}})),Array.isArray(e.insideBoundingBox)&&(t.insideBoundingBox=e.insideBoundingBox.map((function(e){return Array.isArray(e)?e.map((function(e){return parseFloat(e)})):e}))),e.numericRefinements){var r={};Object.keys(e.numericRefinements).forEach((function(t){var n=e.numericRefinements[t]||{};r[t]={},Object.keys(n).forEach((function(e){var i=n[e].map((function(e){return Array.isArray(e)?e.map((function(e){return"string"==typeof e?parseFloat(e):e})):"string"==typeof e?parseFloat(e):e}));r[t][e]=i}))})),t.numericRefinements=r}return n({},e,t)},m.make=function(e){var t=new m(e);return(e.hierarchicalFacets||[]).forEach((function(e){if(e.rootPath){var r=t.getHierarchicalRefinement(e.name);r.length>0&&0!==r[0].indexOf(e.rootPath)&&(t=t.clearRefinements(e.name)),0===(r=t.getHierarchicalRefinement(e.name)).length&&(t=t.toggleHierarchicalFacetRefinement(e.name,e.rootPath))}})),t},m.validate=function(e,t){var r=t||{};return e.tagFilters&&r.tagRefinements&&r.tagRefinements.length>0?new Error("[Tags] Cannot switch from the managed tag API to the advanced API. It is probably an error, if it is really what you want, you should first clear the tags with clearTags method."):e.tagRefinements.length>0&&r.tagFilters?new Error("[Tags] Cannot switch from the advanced tag API to the managed API. It is probably an error, if it is not, you should first clear the tags with clearTags method."):e.numericFilters&&r.numericRefinements&&o(r.numericRefinements)?new Error("[Numeric filters] Can't switch from the advanced to the managed API. It is probably an error, if this is really what you want, you have to first clear the numeric filters."):o(e.numericRefinements)&&r.numericFilters?new Error("[Numeric filters] Can't switch from the managed API to the advanced. It is probably an error, if this is really what you want, you have to first clear the numeric filters."):null},m.prototype={constructor:m,clearRefinements:function(e){var t={numericRefinements:this._clearNumericRefinements(e),facetsRefinements:f.clearRefinement(this.facetsRefinements,e,"conjunctiveFacet"),facetsExcludes:f.clearRefinement(this.facetsExcludes,e,"exclude"),disjunctiveFacetsRefinements:f.clearRefinement(this.disjunctiveFacetsRefinements,e,"disjunctiveFacet"),hierarchicalFacetsRefinements:f.clearRefinement(this.hierarchicalFacetsRefinements,e,"hierarchicalFacet")};return t.numericRefinements===this.numericRefinements&&t.facetsRefinements===this.facetsRefinements&&t.facetsExcludes===this.facetsExcludes&&t.disjunctiveFacetsRefinements===this.disjunctiveFacetsRefinements&&t.hierarchicalFacetsRefinements===this.hierarchicalFacetsRefinements?this:this.setQueryParameters(t)},clearTags:function(){return void 0===this.tagFilters&&0===this.tagRefinements.length?this:this.setQueryParameters({tagFilters:void 0,tagRefinements:[]})},setIndex:function(e){return e===this.index?this:this.setQueryParameters({index:e})},setQuery:function(e){return e===this.query?this:this.setQueryParameters({query:e})},setPage:function(e){return e===this.page?this:this.setQueryParameters({page:e})},setFacets:function(e){return this.setQueryParameters({facets:e})},setDisjunctiveFacets:function(e){return this.setQueryParameters({disjunctiveFacets:e})},setHitsPerPage:function(e){return this.hitsPerPage===e?this:this.setQueryParameters({hitsPerPage:e})},setTypoTolerance:function(e){return this.typoTolerance===e?this:this.setQueryParameters({typoTolerance:e})},addNumericRefinement:function(e,t,r){var i=c(r);if(this.isNumericRefined(e,t,i))return this;var a=n({},this.numericRefinements);return a[e]=n({},a[e]),a[e][t]?(a[e][t]=a[e][t].slice(),a[e][t].push(i)):a[e][t]=[i],this.setQueryParameters({numericRefinements:a})},getConjunctiveRefinements:function(e){return this.isConjunctiveFacet(e)&&this.facetsRefinements[e]||[]},getDisjunctiveRefinements:function(e){return this.isDisjunctiveFacet(e)&&this.disjunctiveFacetsRefinements[e]||[]},getHierarchicalRefinement:function(e){return this.hierarchicalFacetsRefinements[e]||[]},getExcludeRefinements:function(e){return this.isConjunctiveFacet(e)&&this.facetsExcludes[e]||[]},removeNumericRefinement:function(e,t,r){return void 0!==r?this.isNumericRefined(e,t,r)?this.setQueryParameters({numericRefinements:this._clearNumericRefinements((function(n,i){return i===e&&n.op===t&&l(n.val,c(r))}))}):this:void 0!==t?this.isNumericRefined(e,t)?this.setQueryParameters({numericRefinements:this._clearNumericRefinements((function(r,n){return n===e&&r.op===t}))}):this:this.isNumericRefined(e)?this.setQueryParameters({numericRefinements:this._clearNumericRefinements((function(t,r){return r===e}))}):this},getNumericRefinements:function(e){return this.numericRefinements[e]||{}},getNumericRefinement:function(e,t){return this.numericRefinements[e]&&this.numericRefinements[e][t]},_clearNumericRefinements:function(e){if(void 0===e)return o(this.numericRefinements)?{}:this.numericRefinements;if("string"==typeof e)return u(this.numericRefinements,[e]);if("function"==typeof e){var t=!1,r=this.numericRefinements,n=Object.keys(r).reduce((function(n,i){var a=r[i],s={};return a=a||{},Object.keys(a).forEach((function(r){var n=a[r]||[],c=[];n.forEach((function(t){e({val:t,op:r},i,"numeric")||c.push(t)})),c.length!==n.length&&(t=!0),s[r]=c})),n[i]=s,n}),{});return t?n:this.numericRefinements}},addFacet:function(e){return this.isConjunctiveFacet(e)?this:this.setQueryParameters({facets:this.facets.concat([e])})},addDisjunctiveFacet:function(e){return this.isDisjunctiveFacet(e)?this:this.setQueryParameters({disjunctiveFacets:this.disjunctiveFacets.concat([e])})},addHierarchicalFacet:function(e){if(this.isHierarchicalFacet(e.name))throw new Error("Cannot declare two hierarchical facets with the same name: `"+e.name+"`");return this.setQueryParameters({hierarchicalFacets:this.hierarchicalFacets.concat([e])})},addFacetRefinement:function(e,t){if(!this.isConjunctiveFacet(e))throw new Error(e+" is not defined in the facets attribute of the helper configuration");return f.isRefined(this.facetsRefinements,e,t)?this:this.setQueryParameters({facetsRefinements:f.addRefinement(this.facetsRefinements,e,t)})},addExcludeRefinement:function(e,t){if(!this.isConjunctiveFacet(e))throw new Error(e+" is not defined in the facets attribute of the helper configuration");return f.isRefined(this.facetsExcludes,e,t)?this:this.setQueryParameters({facetsExcludes:f.addRefinement(this.facetsExcludes,e,t)})},addDisjunctiveFacetRefinement:function(e,t){if(!this.isDisjunctiveFacet(e))throw new Error(e+" is not defined in the disjunctiveFacets attribute of the helper configuration");return f.isRefined(this.disjunctiveFacetsRefinements,e,t)?this:this.setQueryParameters({disjunctiveFacetsRefinements:f.addRefinement(this.disjunctiveFacetsRefinements,e,t)})},addTagRefinement:function(e){if(this.isTagRefined(e))return this;var t={tagRefinements:this.tagRefinements.concat(e)};return this.setQueryParameters(t)},removeFacet:function(e){return this.isConjunctiveFacet(e)?this.clearRefinements(e).setQueryParameters({facets:this.facets.filter((function(t){return t!==e}))}):this},removeDisjunctiveFacet:function(e){return this.isDisjunctiveFacet(e)?this.clearRefinements(e).setQueryParameters({disjunctiveFacets:this.disjunctiveFacets.filter((function(t){return t!==e}))}):this},removeHierarchicalFacet:function(e){return this.isHierarchicalFacet(e)?this.clearRefinements(e).setQueryParameters({hierarchicalFacets:this.hierarchicalFacets.filter((function(t){return t.name!==e}))}):this},removeFacetRefinement:function(e,t){if(!this.isConjunctiveFacet(e))throw new Error(e+" is not defined in the facets attribute of the helper configuration");return f.isRefined(this.facetsRefinements,e,t)?this.setQueryParameters({facetsRefinements:f.removeRefinement(this.facetsRefinements,e,t)}):this},removeExcludeRefinement:function(e,t){if(!this.isConjunctiveFacet(e))throw new Error(e+" is not defined in the facets attribute of the helper configuration");return f.isRefined(this.facetsExcludes,e,t)?this.setQueryParameters({facetsExcludes:f.removeRefinement(this.facetsExcludes,e,t)}):this},removeDisjunctiveFacetRefinement:function(e,t){if(!this.isDisjunctiveFacet(e))throw new Error(e+" is not defined in the disjunctiveFacets attribute of the helper configuration");return f.isRefined(this.disjunctiveFacetsRefinements,e,t)?this.setQueryParameters({disjunctiveFacetsRefinements:f.removeRefinement(this.disjunctiveFacetsRefinements,e,t)}):this},removeTagRefinement:function(e){if(!this.isTagRefined(e))return this;var t={tagRefinements:this.tagRefinements.filter((function(t){return t!==e}))};return this.setQueryParameters(t)},toggleRefinement:function(e,t){return this.toggleFacetRefinement(e,t)},toggleFacetRefinement:function(e,t){if(this.isHierarchicalFacet(e))return this.toggleHierarchicalFacetRefinement(e,t);if(this.isConjunctiveFacet(e))return this.toggleConjunctiveFacetRefinement(e,t);if(this.isDisjunctiveFacet(e))return this.toggleDisjunctiveFacetRefinement(e,t);throw new Error("Cannot refine the undeclared facet "+e+"; it should be added to the helper options facets, disjunctiveFacets or hierarchicalFacets")},toggleConjunctiveFacetRefinement:function(e,t){if(!this.isConjunctiveFacet(e))throw new Error(e+" is not defined in the facets attribute of the helper configuration");return this.setQueryParameters({facetsRefinements:f.toggleRefinement(this.facetsRefinements,e,t)})},toggleExcludeFacetRefinement:function(e,t){if(!this.isConjunctiveFacet(e))throw new Error(e+" is not defined in the facets attribute of the helper configuration");return this.setQueryParameters({facetsExcludes:f.toggleRefinement(this.facetsExcludes,e,t)})},toggleDisjunctiveFacetRefinement:function(e,t){if(!this.isDisjunctiveFacet(e))throw new Error(e+" is not defined in the disjunctiveFacets attribute of the helper configuration");return this.setQueryParameters({disjunctiveFacetsRefinements:f.toggleRefinement(this.disjunctiveFacetsRefinements,e,t)})},toggleHierarchicalFacetRefinement:function(e,t){if(!this.isHierarchicalFacet(e))throw new Error(e+" is not defined in the hierarchicalFacets attribute of the helper configuration");var r=this._getHierarchicalFacetSeparator(this.getHierarchicalFacetByName(e)),n={};return void 0!==this.hierarchicalFacetsRefinements[e]&&this.hierarchicalFacetsRefinements[e].length>0&&(this.hierarchicalFacetsRefinements[e][0]===t||0===this.hierarchicalFacetsRefinements[e][0].indexOf(t+r))?-1===t.indexOf(r)?n[e]=[]:n[e]=[t.slice(0,t.lastIndexOf(r))]:n[e]=[t],this.setQueryParameters({hierarchicalFacetsRefinements:i({},n,this.hierarchicalFacetsRefinements)})},addHierarchicalFacetRefinement:function(e,t){if(this.isHierarchicalFacetRefined(e))throw new Error(e+" is already refined.");if(!this.isHierarchicalFacet(e))throw new Error(e+" is not defined in the hierarchicalFacets attribute of the helper configuration.");var r={};return r[e]=[t],this.setQueryParameters({hierarchicalFacetsRefinements:i({},r,this.hierarchicalFacetsRefinements)})},removeHierarchicalFacetRefinement:function(e){if(!this.isHierarchicalFacetRefined(e))return this;var t={};return t[e]=[],this.setQueryParameters({hierarchicalFacetsRefinements:i({},t,this.hierarchicalFacetsRefinements)})},toggleTagRefinement:function(e){return this.isTagRefined(e)?this.removeTagRefinement(e):this.addTagRefinement(e)},isDisjunctiveFacet:function(e){return this.disjunctiveFacets.indexOf(e)>-1},isHierarchicalFacet:function(e){return void 0!==this.getHierarchicalFacetByName(e)},isConjunctiveFacet:function(e){return this.facets.indexOf(e)>-1},isFacetRefined:function(e,t){return!!this.isConjunctiveFacet(e)&&f.isRefined(this.facetsRefinements,e,t)},isExcludeRefined:function(e,t){return!!this.isConjunctiveFacet(e)&&f.isRefined(this.facetsExcludes,e,t)},isDisjunctiveFacetRefined:function(e,t){return!!this.isDisjunctiveFacet(e)&&f.isRefined(this.disjunctiveFacetsRefinements,e,t)},isHierarchicalFacetRefined:function(e,t){if(!this.isHierarchicalFacet(e))return!1;var r=this.getHierarchicalRefinement(e);return t?-1!==r.indexOf(t):r.length>0},isNumericRefined:function(e,t,r){if(void 0===r&&void 0===t)return!!this.numericRefinements[e];var n=this.numericRefinements[e]&&void 0!==this.numericRefinements[e][t];if(void 0===r||!n)return n;var i,a,u=c(r),o=void 0!==(i=this.numericRefinements[e][t],a=u,s(i,(function(e){return l(e,a)})));return n&&o},isTagRefined:function(e){return-1!==this.tagRefinements.indexOf(e)},getRefinedDisjunctiveFacets:function(){var e=this,t=a(Object.keys(this.numericRefinements).filter((function(t){return Object.keys(e.numericRefinements[t]).length>0})),this.disjunctiveFacets);return Object.keys(this.disjunctiveFacetsRefinements).filter((function(t){return e.disjunctiveFacetsRefinements[t].length>0})).concat(t).concat(this.getRefinedHierarchicalFacets())},getRefinedHierarchicalFacets:function(){var e=this;return a(this.hierarchicalFacets.map((function(e){return e.name})),Object.keys(this.hierarchicalFacetsRefinements).filter((function(t){return e.hierarchicalFacetsRefinements[t].length>0})))},getUnrefinedDisjunctiveFacets:function(){var e=this.getRefinedDisjunctiveFacets();return this.disjunctiveFacets.filter((function(t){return-1===e.indexOf(t)}))},managedParameters:["index","facets","disjunctiveFacets","facetsRefinements","hierarchicalFacets","facetsExcludes","disjunctiveFacetsRefinements","numericRefinements","tagRefinements","hierarchicalFacetsRefinements"],getQueryParams:function(){var e=this.managedParameters,t={},r=this;return Object.keys(this).forEach((function(n){var i=r[n];-1===e.indexOf(n)&&void 0!==i&&(t[n]=i)})),t},setQueryParameter:function(e,t){if(this[e]===t)return this;var r={};return r[e]=t,this.setQueryParameters(r)},setQueryParameters:function(e){if(!e)return this;var t=m.validate(this,e);if(t)throw t;var r=this,n=m._parseNumbers(e),i=Object.keys(this).reduce((function(e,t){return e[t]=r[t],e}),{}),a=Object.keys(n).reduce((function(e,t){var r=void 0!==e[t],i=void 0!==n[t];return r&&!i?u(e,[t]):(i&&(e[t]=n[t]),e)}),i);return new this.constructor(a)},resetPage:function(){return void 0===this.page?this:this.setPage(0)},_getHierarchicalFacetSortBy:function(e){return e.sortBy||["isRefined:desc","name:asc"]},_getHierarchicalFacetSeparator:function(e){return e.separator||" > "},_getHierarchicalRootPath:function(e){return e.rootPath||null},_getHierarchicalShowParentLevel:function(e){return"boolean"!=typeof e.showParentLevel||e.showParentLevel},getHierarchicalFacetByName:function(e){return s(this.hierarchicalFacets,(function(t){return t.name===e}))},getHierarchicalFacetBreadcrumb:function(e){if(!this.isHierarchicalFacet(e))return[];var t=this.getHierarchicalRefinement(e)[0];if(!t)return[];var r=this._getHierarchicalFacetSeparator(this.getHierarchicalFacetByName(e));return t.split(r).map((function(e){return e.trim()}))},toString:function(){return JSON.stringify(this,null,2)}},e.exports=m},210:(e,t,r)=>{"use strict";e.exports=function(e){return function(t,r){var s=e.hierarchicalFacets[r],o=e.hierarchicalFacetsRefinements[s.name]&&e.hierarchicalFacetsRefinements[s.name][0]||"",h=e._getHierarchicalFacetSeparator(s),f=e._getHierarchicalRootPath(s),l=e._getHierarchicalShowParentLevel(s),m=a(e._getHierarchicalFacetSortBy(s)),d=t.every((function(e){return e.exhaustive})),p=function(e,t,r,a,s){return function(o,h,f){var l=o;if(f>0){var m=0;for(l=o;m{"use strict";var n=r(185),i=r(2344),a=r(2148),s=r(4587),c=r(7888),u=r(9725),o=r(2293),h=r(4039),f=h.escapeFacetValue,l=h.unescapeFacetValue,m=r(210);function d(e){var t={};return e.forEach((function(e,r){t[e]=r})),t}function p(e,t,r){t&&t[r]&&(e.stats=t[r])}function v(e,t,r){var a=t[0];this._rawResults=t;var o=this;Object.keys(a).forEach((function(e){o[e]=a[e]})),Object.keys(r||{}).forEach((function(e){o[e]=r[e]})),this.processingTimeMS=t.reduce((function(e,t){return void 0===t.processingTimeMS?e:e+t.processingTimeMS}),0),this.disjunctiveFacets=[],this.hierarchicalFacets=e.hierarchicalFacets.map((function(){return[]})),this.facets=[];var h=e.getRefinedDisjunctiveFacets(),f=d(e.facets),v=d(e.disjunctiveFacets),g=1,y=a.facets||{};Object.keys(y).forEach((function(t){var r,n,i=y[t],s=(r=e.hierarchicalFacets,n=t,c(r,(function(e){return(e.attributes||[]).indexOf(n)>-1})));if(s){var h=s.attributes.indexOf(t),l=u(e.hierarchicalFacets,(function(e){return e.name===s.name}));o.hierarchicalFacets[l][h]={attribute:t,data:i,exhaustive:a.exhaustiveFacetsCount}}else{var m,d=-1!==e.disjunctiveFacets.indexOf(t),g=-1!==e.facets.indexOf(t);d&&(m=v[t],o.disjunctiveFacets[m]={name:t,data:i,exhaustive:a.exhaustiveFacetsCount},p(o.disjunctiveFacets[m],a.facets_stats,t)),g&&(m=f[t],o.facets[m]={name:t,data:i,exhaustive:a.exhaustiveFacetsCount},p(o.facets[m],a.facets_stats,t))}})),this.hierarchicalFacets=s(this.hierarchicalFacets),h.forEach((function(r){var s=t[g],c=s&&s.facets?s.facets:{},h=e.getHierarchicalFacetByName(r);Object.keys(c).forEach((function(t){var r,f=c[t];if(h){r=u(e.hierarchicalFacets,(function(e){return e.name===h.name}));var m=u(o.hierarchicalFacets[r],(function(e){return e.attribute===t}));if(-1===m)return;o.hierarchicalFacets[r][m].data=n({},o.hierarchicalFacets[r][m].data,f)}else{r=v[t];var d=a.facets&&a.facets[t]||{};o.disjunctiveFacets[r]={name:t,data:i({},f,d),exhaustive:s.exhaustiveFacetsCount},p(o.disjunctiveFacets[r],s.facets_stats,t),e.disjunctiveFacetsRefinements[t]&&e.disjunctiveFacetsRefinements[t].forEach((function(n){!o.disjunctiveFacets[r].data[n]&&e.disjunctiveFacetsRefinements[t].indexOf(l(n))>-1&&(o.disjunctiveFacets[r].data[n]=0)}))}})),g++})),e.getRefinedHierarchicalFacets().forEach((function(r){var n=e.getHierarchicalFacetByName(r),a=e._getHierarchicalFacetSeparator(n),s=e.getHierarchicalRefinement(r);0===s.length||s[0].split(a).length<2||t.slice(g).forEach((function(t){var r=t&&t.facets?t.facets:{};Object.keys(r).forEach((function(t){var c=r[t],h=u(e.hierarchicalFacets,(function(e){return e.name===n.name})),f=u(o.hierarchicalFacets[h],(function(e){return e.attribute===t}));if(-1!==f){var l={};if(s.length>0){var m=s[0].split(a)[0];l[m]=o.hierarchicalFacets[h][f].data[m]}o.hierarchicalFacets[h][f].data=i(l,c,o.hierarchicalFacets[h][f].data)}})),g++}))})),Object.keys(e.facetsExcludes).forEach((function(t){var r=e.facetsExcludes[t],n=f[t];o.facets[n]={name:t,data:a.facets[t],exhaustive:a.exhaustiveFacetsCount},r.forEach((function(e){o.facets[n]=o.facets[n]||{name:t},o.facets[n].data=o.facets[n].data||{},o.facets[n].data[e]=0}))})),this.hierarchicalFacets=this.hierarchicalFacets.map(m(e)),this.facets=s(this.facets),this.disjunctiveFacets=s(this.disjunctiveFacets),this._state=e}function g(e,t,r,n){if(n=n||0,Array.isArray(t))return e(t,r[n]);if(!t.data||0===t.data.length)return t;var a=t.data.map((function(t){return g(e,t,r,n+1)})),s=e(a,r[n]);return i({data:s},t)}function y(e,t){var r=c(e,(function(e){return e.name===t}));return r&&r.stats}function R(e,t,r,n,i){var a=c(i,(function(e){return e.name===r})),s=a&&a.data&&a.data[n]?a.data[n]:0,u=a&&a.exhaustive||!1;return{type:t,attributeName:r,name:n,count:s,exhaustive:u}}v.prototype.getFacetByName=function(e){function t(t){return t.name===e}return c(this.facets,t)||c(this.disjunctiveFacets,t)||c(this.hierarchicalFacets,t)},v.DEFAULT_SORT=["isRefined:desc","count:desc","name:asc"],v.prototype.getFacetValues=function(e,t){var r=function(e,t){function r(e){return e.name===t}if(e._state.isConjunctiveFacet(t)){var n=c(e.facets,r);return n?Object.keys(n.data).map((function(r){var i=f(r);return{name:r,escapedValue:i,count:n.data[r],isRefined:e._state.isFacetRefined(t,i),isExcluded:e._state.isExcludeRefined(t,r)}})):[]}if(e._state.isDisjunctiveFacet(t)){var i=c(e.disjunctiveFacets,r);return i?Object.keys(i.data).map((function(r){var n=f(r);return{name:r,escapedValue:n,count:i.data[r],isRefined:e._state.isDisjunctiveFacetRefined(t,n)}})):[]}if(e._state.isHierarchicalFacet(t))return c(e.hierarchicalFacets,r)}(this,e);if(r){var n,s=i({},t,{sortBy:v.DEFAULT_SORT,facetOrdering:!(t&&t.sortBy)}),u=this;if(Array.isArray(r))n=[e];else n=u._state.getHierarchicalFacetByName(r.name).attributes;return g((function(e,t){if(s.facetOrdering){var r=function(e,t){return e.renderingContent&&e.renderingContent.facetOrdering&&e.renderingContent.facetOrdering.values&&e.renderingContent.facetOrdering.values[t]}(u,t);if(Boolean(r))return function(e,t){var r=[],n=[],i=(t.order||[]).reduce((function(e,t,r){return e[t]=r,e}),{});e.forEach((function(e){var t=e.path||e.name;void 0!==i[t]?r[i[t]]=e:n.push(e)})),r=r.filter((function(e){return e}));var s,c=t.sortRemainingBy;return"hidden"===c?r:(s="alpha"===c?[["path","name"],["asc","asc"]]:[["count"],["desc"]],r.concat(a(n,s[0],s[1])))}(e,r)}if(Array.isArray(s.sortBy)){var n=o(s.sortBy,v.DEFAULT_SORT);return a(e,n[0],n[1])}if("function"==typeof s.sortBy)return function(e,t){return t.sort(e)}(s.sortBy,e);throw new Error("options.sortBy is optional but if defined it must be either an array of string (predicates) or a sorting function")}),r,n)}},v.prototype.getFacetStats=function(e){return this._state.isConjunctiveFacet(e)?y(this.facets,e):this._state.isDisjunctiveFacet(e)?y(this.disjunctiveFacets,e):void 0},v.prototype.getRefinements=function(){var e=this._state,t=this,r=[];return Object.keys(e.facetsRefinements).forEach((function(n){e.facetsRefinements[n].forEach((function(i){r.push(R(e,"facet",n,i,t.facets))}))})),Object.keys(e.facetsExcludes).forEach((function(n){e.facetsExcludes[n].forEach((function(i){r.push(R(e,"exclude",n,i,t.facets))}))})),Object.keys(e.disjunctiveFacetsRefinements).forEach((function(n){e.disjunctiveFacetsRefinements[n].forEach((function(i){r.push(R(e,"disjunctive",n,i,t.disjunctiveFacets))}))})),Object.keys(e.hierarchicalFacetsRefinements).forEach((function(n){e.hierarchicalFacetsRefinements[n].forEach((function(i){r.push(function(e,t,r,n){var i=e.getHierarchicalFacetByName(t),a=e._getHierarchicalFacetSeparator(i),s=r.split(a),u=c(n,(function(e){return e.name===t})),o=s.reduce((function(e,t){var r=e&&c(e.data,(function(e){return e.name===t}));return void 0!==r?r:e}),u),h=o&&o.count||0,f=o&&o.exhaustive||!1,l=o&&o.path||"";return{type:"hierarchical",attributeName:t,name:l,count:h,exhaustive:f}}(e,n,i,t.hierarchicalFacets))}))})),Object.keys(e.numericRefinements).forEach((function(t){var n=e.numericRefinements[t];Object.keys(n).forEach((function(e){n[e].forEach((function(n){r.push({type:"numeric",attributeName:t,name:n,numericValue:n,operator:e})}))}))})),e.tagRefinements.forEach((function(e){r.push({type:"tag",attributeName:"_tags",name:e})})),r},e.exports=v},9374:(e,t,r)=>{"use strict";var n=r(7775),i=r(3076),a=r(8078),s=r(6394),c=r(7331),u=r(4853),o=r(116),h=r(9803),f=r(185),l=r(4336),m=r(4039).escapeFacetValue;function d(e,t,r){"function"==typeof e.addAlgoliaAgent&&e.addAlgoliaAgent("JS Helper ("+l+")"),this.setClient(e);var i=r||{};i.index=t,this.state=n.make(i),this.lastResults=null,this._queryId=0,this._lastQueryIdReceived=-1,this.derivedHelpers=[],this._currentNbQueries=0}function p(e){if(e<0)throw new Error("Page requested below 0.");return this._change({state:this.state.setPage(e),isPageReset:!1}),this}function v(){return this.state.page}u(d,c),d.prototype.search=function(){return this._search({onlyWithDerivedHelpers:!1}),this},d.prototype.searchOnlyWithDerivedHelpers=function(){return this._search({onlyWithDerivedHelpers:!0}),this},d.prototype.getQuery=function(){var e=this.state;return s._getHitsSearchParams(e)},d.prototype.searchOnce=function(e,t){var r=e?this.state.setQueryParameters(e):this.state,n=s._getQueries(r.index,r),a=this;if(this._currentNbQueries++,this.emit("searchOnce",{state:r}),!t)return this.client.search(n).then((function(e){return a._currentNbQueries--,0===a._currentNbQueries&&a.emit("searchQueueEmpty"),{content:new i(r,e.results),state:r,_originalResponse:e}}),(function(e){throw a._currentNbQueries--,0===a._currentNbQueries&&a.emit("searchQueueEmpty"),e}));this.client.search(n).then((function(e){a._currentNbQueries--,0===a._currentNbQueries&&a.emit("searchQueueEmpty"),t(null,new i(r,e.results),r)})).catch((function(e){a._currentNbQueries--,0===a._currentNbQueries&&a.emit("searchQueueEmpty"),t(e,null,r)}))},d.prototype.findAnswers=function(e){var t=this.state,r=this.derivedHelpers[0];if(!r)return Promise.resolve([]);var n=r.getModifiedState(t),i=f({attributesForPrediction:e.attributesForPrediction,nbHits:e.nbHits},{params:h(s._getHitsSearchParams(n),["attributesToSnippet","hitsPerPage","restrictSearchableAttributes","snippetEllipsisText"])}),a="search for answers was called, but this client does not have a function client.initIndex(index).findAnswers";if("function"!=typeof this.client.initIndex)throw new Error(a);var c=this.client.initIndex(n.index);if("function"!=typeof c.findAnswers)throw new Error(a);return c.findAnswers(n.query,e.queryLanguages,i)},d.prototype.searchForFacetValues=function(e,t,r,n){var i="function"==typeof this.client.searchForFacetValues,a="function"==typeof this.client.initIndex;if(!i&&!a&&"function"!=typeof this.client.search)throw new Error("search for facet values (searchable) was called, but this client does not have a function client.searchForFacetValues or client.initIndex(index).searchForFacetValues");var c=this.state.setQueryParameters(n||{}),u=c.isDisjunctiveFacet(e),o=s.getSearchForFacetQuery(e,t,r,c);this._currentNbQueries++;var h,f=this;return i?h=this.client.searchForFacetValues([{indexName:c.index,params:o}]):a?h=this.client.initIndex(c.index).searchForFacetValues(o):(delete o.facetName,h=this.client.search([{type:"facet",facet:e,indexName:c.index,params:o}]).then((function(e){return e.results[0]}))),this.emit("searchForFacetValues",{state:c,facet:e,query:t}),h.then((function(t){return f._currentNbQueries--,0===f._currentNbQueries&&f.emit("searchQueueEmpty"),(t=Array.isArray(t)?t[0]:t).facetHits.forEach((function(t){t.escapedValue=m(t.value),t.isRefined=u?c.isDisjunctiveFacetRefined(e,t.escapedValue):c.isFacetRefined(e,t.escapedValue)})),t}),(function(e){throw f._currentNbQueries--,0===f._currentNbQueries&&f.emit("searchQueueEmpty"),e}))},d.prototype.setQuery=function(e){return this._change({state:this.state.resetPage().setQuery(e),isPageReset:!0}),this},d.prototype.clearRefinements=function(e){return this._change({state:this.state.resetPage().clearRefinements(e),isPageReset:!0}),this},d.prototype.clearTags=function(){return this._change({state:this.state.resetPage().clearTags(),isPageReset:!0}),this},d.prototype.addDisjunctiveFacetRefinement=function(e,t){return this._change({state:this.state.resetPage().addDisjunctiveFacetRefinement(e,t),isPageReset:!0}),this},d.prototype.addDisjunctiveRefine=function(){return this.addDisjunctiveFacetRefinement.apply(this,arguments)},d.prototype.addHierarchicalFacetRefinement=function(e,t){return this._change({state:this.state.resetPage().addHierarchicalFacetRefinement(e,t),isPageReset:!0}),this},d.prototype.addNumericRefinement=function(e,t,r){return this._change({state:this.state.resetPage().addNumericRefinement(e,t,r),isPageReset:!0}),this},d.prototype.addFacetRefinement=function(e,t){return this._change({state:this.state.resetPage().addFacetRefinement(e,t),isPageReset:!0}),this},d.prototype.addRefine=function(){return this.addFacetRefinement.apply(this,arguments)},d.prototype.addFacetExclusion=function(e,t){return this._change({state:this.state.resetPage().addExcludeRefinement(e,t),isPageReset:!0}),this},d.prototype.addExclude=function(){return this.addFacetExclusion.apply(this,arguments)},d.prototype.addTag=function(e){return this._change({state:this.state.resetPage().addTagRefinement(e),isPageReset:!0}),this},d.prototype.removeNumericRefinement=function(e,t,r){return this._change({state:this.state.resetPage().removeNumericRefinement(e,t,r),isPageReset:!0}),this},d.prototype.removeDisjunctiveFacetRefinement=function(e,t){return this._change({state:this.state.resetPage().removeDisjunctiveFacetRefinement(e,t),isPageReset:!0}),this},d.prototype.removeDisjunctiveRefine=function(){return this.removeDisjunctiveFacetRefinement.apply(this,arguments)},d.prototype.removeHierarchicalFacetRefinement=function(e){return this._change({state:this.state.resetPage().removeHierarchicalFacetRefinement(e),isPageReset:!0}),this},d.prototype.removeFacetRefinement=function(e,t){return this._change({state:this.state.resetPage().removeFacetRefinement(e,t),isPageReset:!0}),this},d.prototype.removeRefine=function(){return this.removeFacetRefinement.apply(this,arguments)},d.prototype.removeFacetExclusion=function(e,t){return this._change({state:this.state.resetPage().removeExcludeRefinement(e,t),isPageReset:!0}),this},d.prototype.removeExclude=function(){return this.removeFacetExclusion.apply(this,arguments)},d.prototype.removeTag=function(e){return this._change({state:this.state.resetPage().removeTagRefinement(e),isPageReset:!0}),this},d.prototype.toggleFacetExclusion=function(e,t){return this._change({state:this.state.resetPage().toggleExcludeFacetRefinement(e,t),isPageReset:!0}),this},d.prototype.toggleExclude=function(){return this.toggleFacetExclusion.apply(this,arguments)},d.prototype.toggleRefinement=function(e,t){return this.toggleFacetRefinement(e,t)},d.prototype.toggleFacetRefinement=function(e,t){return this._change({state:this.state.resetPage().toggleFacetRefinement(e,t),isPageReset:!0}),this},d.prototype.toggleRefine=function(){return this.toggleFacetRefinement.apply(this,arguments)},d.prototype.toggleTag=function(e){return this._change({state:this.state.resetPage().toggleTagRefinement(e),isPageReset:!0}),this},d.prototype.nextPage=function(){var e=this.state.page||0;return this.setPage(e+1)},d.prototype.previousPage=function(){var e=this.state.page||0;return this.setPage(e-1)},d.prototype.setCurrentPage=p,d.prototype.setPage=p,d.prototype.setIndex=function(e){return this._change({state:this.state.resetPage().setIndex(e),isPageReset:!0}),this},d.prototype.setQueryParameter=function(e,t){return this._change({state:this.state.resetPage().setQueryParameter(e,t),isPageReset:!0}),this},d.prototype.setState=function(e){return this._change({state:n.make(e),isPageReset:!1}),this},d.prototype.overrideStateWithoutTriggeringChangeEvent=function(e){return this.state=new n(e),this},d.prototype.hasRefinements=function(e){return!!o(this.state.getNumericRefinements(e))||(this.state.isConjunctiveFacet(e)?this.state.isFacetRefined(e):this.state.isDisjunctiveFacet(e)?this.state.isDisjunctiveFacetRefined(e):!!this.state.isHierarchicalFacet(e)&&this.state.isHierarchicalFacetRefined(e))},d.prototype.isExcluded=function(e,t){return this.state.isExcludeRefined(e,t)},d.prototype.isDisjunctiveRefined=function(e,t){return this.state.isDisjunctiveFacetRefined(e,t)},d.prototype.hasTag=function(e){return this.state.isTagRefined(e)},d.prototype.isTagRefined=function(){return this.hasTagRefinements.apply(this,arguments)},d.prototype.getIndex=function(){return this.state.index},d.prototype.getCurrentPage=v,d.prototype.getPage=v,d.prototype.getTags=function(){return this.state.tagRefinements},d.prototype.getRefinements=function(e){var t=[];if(this.state.isConjunctiveFacet(e))this.state.getConjunctiveRefinements(e).forEach((function(e){t.push({value:e,type:"conjunctive"})})),this.state.getExcludeRefinements(e).forEach((function(e){t.push({value:e,type:"exclude"})}));else if(this.state.isDisjunctiveFacet(e)){this.state.getDisjunctiveRefinements(e).forEach((function(e){t.push({value:e,type:"disjunctive"})}))}var r=this.state.getNumericRefinements(e);return Object.keys(r).forEach((function(e){var n=r[e];t.push({value:n,operator:e,type:"numeric"})})),t},d.prototype.getNumericRefinement=function(e,t){return this.state.getNumericRefinement(e,t)},d.prototype.getHierarchicalFacetBreadcrumb=function(e){return this.state.getHierarchicalFacetBreadcrumb(e)},d.prototype._search=function(e){var t=this.state,r=[],n=[];e.onlyWithDerivedHelpers||(n=s._getQueries(t.index,t),r.push({state:t,queriesCount:n.length,helper:this}),this.emit("search",{state:t,results:this.lastResults}));var i=this.derivedHelpers.map((function(e){var n=e.getModifiedState(t),i=s._getQueries(n.index,n);return r.push({state:n,queriesCount:i.length,helper:e}),e.emit("search",{state:n,results:e.lastResults}),i})),a=Array.prototype.concat.apply(n,i),c=this._queryId++;this._currentNbQueries++;try{this.client.search(a).then(this._dispatchAlgoliaResponse.bind(this,r,c)).catch(this._dispatchAlgoliaError.bind(this,c))}catch(u){this.emit("error",{error:u})}},d.prototype._dispatchAlgoliaResponse=function(e,t,r){if(!(t0},d.prototype._change=function(e){var t=e.state,r=e.isPageReset;t!==this.state&&(this.state=t,this.emit("change",{state:this.state,results:this.lastResults,isPageReset:r}))},d.prototype.clearCache=function(){return this.client.clearCache&&this.client.clearCache(),this},d.prototype.setClient=function(e){return this.client===e||("function"==typeof e.addAlgoliaAgent&&e.addAlgoliaAgent("JS Helper ("+l+")"),this.client=e),this},d.prototype.getClient=function(){return this.client},d.prototype.derive=function(e){var t=new a(this,e);return this.derivedHelpers.push(t),t},d.prototype.detachDerivedHelper=function(e){var t=this.derivedHelpers.indexOf(e);if(-1===t)throw new Error("Derived helper already detached");this.derivedHelpers.splice(t,1)},d.prototype.hasPendingRequests=function(){return this._currentNbQueries>0},e.exports=d},4587:e=>{"use strict";e.exports=function(e){return Array.isArray(e)?e.filter(Boolean):[]}},2344:e=>{"use strict";e.exports=function(){var e=Array.prototype.slice.call(arguments);return e.reduceRight((function(e,t){return Object.keys(Object(t)).forEach((function(r){void 0!==t[r]&&(void 0!==e[r]&&delete e[r],e[r]=t[r])})),e}),{})}},4039:e=>{"use strict";e.exports={escapeFacetValue:function(e){return"string"!=typeof e?e:String(e).replace(/^-/,"\\-")},unescapeFacetValue:function(e){return"string"!=typeof e?e:e.replace(/^\\-/,"-")}}},7888:e=>{"use strict";e.exports=function(e,t){if(Array.isArray(e))for(var r=0;r{"use strict";e.exports=function(e,t){if(!Array.isArray(e))return-1;for(var r=0;r{"use strict";var n=r(7888);e.exports=function(e,t){var r=(t||[]).map((function(e){return e.split(":")}));return e.reduce((function(e,t){var i=t.split(":"),a=n(r,(function(e){return e[0]===i[0]}));return i.length>1||!a?(e[0].push(i[0]),e[1].push(i[1]),e):(e[0].push(a[0]),e[1].push(a[1]),e)}),[[],[]])}},4853:e=>{"use strict";e.exports=function(e,t){e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}},2686:e=>{"use strict";e.exports=function(e,t){return e.filter((function(r,n){return t.indexOf(r)>-1&&e.indexOf(r)===n}))}},185:e=>{"use strict";function t(e){return"function"==typeof e||Array.isArray(e)||"[object Object]"===Object.prototype.toString.call(e)}function r(e,n){if(e===n)return e;for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)&&"__proto__"!==i){var a=n[i],s=e[i];void 0!==s&&void 0===a||(t(s)&&t(a)?e[i]=r(s,a):e[i]="object"==typeof(c=a)&&null!==c?r(Array.isArray(c)?[]:{},c):c)}var c;return e}e.exports=function(e){t(e)||(e={});for(var n=1,i=arguments.length;n{"use strict";e.exports=function(e){return e&&Object.keys(e).length>0}},9803:e=>{"use strict";e.exports=function(e,t){if(null===e)return{};var r,n,i={},a=Object.keys(e);for(n=0;n=0||(i[r]=e[r]);return i}},2148:e=>{"use strict";function t(e,t){if(e!==t){var r=void 0!==e,n=null===e,i=void 0!==t,a=null===t;if(!a&&e>t||n&&i||!r)return 1;if(!n&&e=n.length?a:"desc"===n[i]?-a:a}return e.index-r.index})),i.map((function(e){return e.value}))}},8023:e=>{"use strict";e.exports=function e(t){if("number"==typeof t)return t;if("string"==typeof t)return parseFloat(t);if(Array.isArray(t))return t.map(e);throw new Error("The value should be a number, a parsable string or an array of those.")}},6394:(e,t,r)=>{"use strict";var n=r(185);function i(e){return Object.keys(e).sort((function(e,t){return e.localeCompare(t)})).reduce((function(t,r){return t[r]=e[r],t}),{})}var a={_getQueries:function(e,t){var r=[];return r.push({indexName:e,params:a._getHitsSearchParams(t)}),t.getRefinedDisjunctiveFacets().forEach((function(n){r.push({indexName:e,params:a._getDisjunctiveFacetSearchParams(t,n)})})),t.getRefinedHierarchicalFacets().forEach((function(n){var i=t.getHierarchicalFacetByName(n),s=t.getHierarchicalRefinement(n),c=t._getHierarchicalFacetSeparator(i);if(s.length>0&&s[0].split(c).length>1){var u=s[0].split(c).slice(0,-1).reduce((function(e,t,r){return e.concat({attribute:i.attributes[r],value:0===r?t:[e[e.length-1].value,t].join(c)})}),[]);u.forEach((function(n,s){var c=a._getDisjunctiveFacetSearchParams(t,n.attribute,0===s);function o(e){return i.attributes.some((function(t){return t===e.split(":")[0]}))}var h=(c.facetFilters||[]).reduce((function(e,t){if(Array.isArray(t)){var r=t.filter((function(e){return!o(e)}));r.length>0&&e.push(r)}return"string"!=typeof t||o(t)||e.push(t),e}),[]),f=u[s-1];c.facetFilters=s>0?h.concat(f.attribute+":"+f.value):h.length>0?h:void 0,r.push({indexName:e,params:c})}))}})),r},_getHitsSearchParams:function(e){var t=e.facets.concat(e.disjunctiveFacets).concat(a._getHitsHierarchicalFacetsAttributes(e)),r=a._getFacetFilters(e),s=a._getNumericFilters(e),c=a._getTagFilters(e),u={facets:t.indexOf("*")>-1?["*"]:t,tagFilters:c};return r.length>0&&(u.facetFilters=r),s.length>0&&(u.numericFilters=s),i(n({},e.getQueryParams(),u))},_getDisjunctiveFacetSearchParams:function(e,t,r){var s=a._getFacetFilters(e,t,r),c=a._getNumericFilters(e,t),u=a._getTagFilters(e),o={hitsPerPage:0,page:0,analytics:!1,clickAnalytics:!1};u.length>0&&(o.tagFilters=u);var h=e.getHierarchicalFacetByName(t);return o.facets=h?a._getDisjunctiveHierarchicalFacetAttribute(e,h,r):t,c.length>0&&(o.numericFilters=c),s.length>0&&(o.facetFilters=s),i(n({},e.getQueryParams(),o))},_getNumericFilters:function(e,t){if(e.numericFilters)return e.numericFilters;var r=[];return Object.keys(e.numericRefinements).forEach((function(n){var i=e.numericRefinements[n]||{};Object.keys(i).forEach((function(e){var a=i[e]||[];t!==n&&a.forEach((function(t){if(Array.isArray(t)){var i=t.map((function(t){return n+e+t}));r.push(i)}else r.push(n+e+t)}))}))})),r},_getTagFilters:function(e){return e.tagFilters?e.tagFilters:e.tagRefinements.join(",")},_getFacetFilters:function(e,t,r){var n=[],i=e.facetsRefinements||{};Object.keys(i).forEach((function(e){(i[e]||[]).forEach((function(t){n.push(e+":"+t)}))}));var a=e.facetsExcludes||{};Object.keys(a).forEach((function(e){(a[e]||[]).forEach((function(t){n.push(e+":-"+t)}))}));var s=e.disjunctiveFacetsRefinements||{};Object.keys(s).forEach((function(e){var r=s[e]||[];if(e!==t&&r&&0!==r.length){var i=[];r.forEach((function(t){i.push(e+":"+t)})),n.push(i)}}));var c=e.hierarchicalFacetsRefinements||{};return Object.keys(c).forEach((function(i){var a=(c[i]||[])[0];if(void 0!==a){var s,u,o=e.getHierarchicalFacetByName(i),h=e._getHierarchicalFacetSeparator(o),f=e._getHierarchicalRootPath(o);if(t===i){if(-1===a.indexOf(h)||!f&&!0===r||f&&f.split(h).length===a.split(h).length)return;f?(u=f.split(h).length-1,a=f):(u=a.split(h).length-2,a=a.slice(0,a.lastIndexOf(h))),s=o.attributes[u]}else u=a.split(h).length-1,s=o.attributes[u];s&&n.push([s+":"+a])}})),n},_getHitsHierarchicalFacetsAttributes:function(e){return e.hierarchicalFacets.reduce((function(t,r){var n=e.getHierarchicalRefinement(r.name)[0];if(!n)return t.push(r.attributes[0]),t;var i=e._getHierarchicalFacetSeparator(r),a=n.split(i).length,s=r.attributes.slice(0,a+1);return t.concat(s)}),[])},_getDisjunctiveHierarchicalFacetAttribute:function(e,t,r){var n=e._getHierarchicalFacetSeparator(t);if(!0===r){var i=e._getHierarchicalRootPath(t),a=0;return i&&(a=i.split(n).length),[t.attributes[a]]}var s=(e.getHierarchicalRefinement(t.name)[0]||"").split(n).length-1;return t.attributes.slice(0,s+1)},getSearchForFacetQuery:function(e,t,r,s){var c=s.isDisjunctiveFacet(e)?s.clearRefinements(e):s,u={facetQuery:t,facetName:e};return"number"==typeof r&&(u.maxFacetHits=r),i(n({},a._getHitsSearchParams(c),u))}};e.exports=a},6801:e=>{"use strict";e.exports=function(e){return null!==e&&/^[a-zA-Z0-9_-]{1,64}$/.test(e)}},4336:e=>{"use strict";e.exports="3.11.1"},290:function(e){e.exports=function(){"use strict";function e(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function t(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function r(r){for(var n=1;n=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}function i(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e)){var r=[],n=!0,i=!1,a=void 0;try{for(var s,c=e[Symbol.iterator]();!(n=(s=c.next()).done)&&(r.push(s.value),!t||r.length!==t);n=!0);}catch(e){i=!0,a=e}finally{try{n||null==c.return||c.return()}finally{if(i)throw a}}return r}}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function a(e){return function(e){if(Array.isArray(e)){for(var t=0,r=new Array(e.length);t2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}};return Promise.resolve().then((function(){var r=JSON.stringify(e),n=a()[r];return Promise.all([n||t(),void 0!==n])})).then((function(e){var t=i(e,2),n=t[0],a=t[1];return Promise.all([n,a||r.miss(n)])})).then((function(e){return i(e,1)[0]}))},set:function(e,t){return Promise.resolve().then((function(){var i=a();return i[JSON.stringify(e)]=t,n().setItem(r,JSON.stringify(i)),t}))},delete:function(e){return Promise.resolve().then((function(){var t=a();delete t[JSON.stringify(e)],n().setItem(r,JSON.stringify(t))}))},clear:function(){return Promise.resolve().then((function(){n().removeItem(r)}))}}}function c(e){var t=a(e.caches),r=t.shift();return void 0===r?{get:function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}};return t().then((function(e){return Promise.all([e,r.miss(e)])})).then((function(e){return i(e,1)[0]}))},set:function(e,t){return Promise.resolve(t)},delete:function(e){return Promise.resolve()},clear:function(){return Promise.resolve()}}:{get:function(e,n){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}};return r.get(e,n,i).catch((function(){return c({caches:t}).get(e,n,i)}))},set:function(e,n){return r.set(e,n).catch((function(){return c({caches:t}).set(e,n)}))},delete:function(e){return r.delete(e).catch((function(){return c({caches:t}).delete(e)}))},clear:function(){return r.clear().catch((function(){return c({caches:t}).clear()}))}}}function u(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{serializable:!0},t={};return{get:function(r,n){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{miss:function(){return Promise.resolve()}},a=JSON.stringify(r);if(a in t)return Promise.resolve(e.serializable?JSON.parse(t[a]):t[a]);var s=n(),c=i&&i.miss||function(){return Promise.resolve()};return s.then((function(e){return c(e)})).then((function(){return s}))},set:function(r,n){return t[JSON.stringify(r)]=e.serializable?JSON.stringify(n):n,Promise.resolve(n)},delete:function(e){return delete t[JSON.stringify(e)],Promise.resolve()},clear:function(){return t={},Promise.resolve()}}}function o(e){for(var t=e.length-1;t>0;t--){var r=Math.floor(Math.random()*(t+1)),n=e[t];e[t]=e[r],e[r]=n}return e}function h(e,t){return t?(Object.keys(t).forEach((function(r){e[r]=t[r](e)})),e):e}function f(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),n=1;n0?n:void 0,timeout:r.timeout||t,headers:r.headers||{},queryParameters:r.queryParameters||{},cacheable:r.cacheable}}var d={Read:1,Write:2,Any:3},p=1,v=2,g=3;function y(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:p;return r(r({},e),{},{status:t,lastUpdate:Date.now()})}function R(e){return"string"==typeof e?{protocol:"https",url:e,accept:d.Any}:{protocol:e.protocol||"https",url:e.url,accept:e.accept||d.Any}}var F="GET",b="POST";function P(e,t){return Promise.all(t.map((function(t){return e.get(t,(function(){return Promise.resolve(y(t))}))}))).then((function(e){var r=e.filter((function(e){return function(e){return e.status===p||Date.now()-e.lastUpdate>12e4}(e)})),n=e.filter((function(e){return function(e){return e.status===g&&Date.now()-e.lastUpdate<=12e4}(e)})),i=[].concat(a(r),a(n));return{getTimeout:function(e,t){return(0===n.length&&0===e?1:n.length+3+e)*t},statelessHosts:i.length>0?i.map((function(e){return R(e)})):t}}))}function j(e,t,n,i){var s=[],c=function(e,t){if(e.method!==F&&(void 0!==e.data||void 0!==t.data)){var n=Array.isArray(e.data)?e.data:r(r({},e.data),t.data);return JSON.stringify(n)}}(n,i),u=function(e,t){var n=r(r({},e.headers),t.headers),i={};return Object.keys(n).forEach((function(e){var t=n[e];i[e.toLowerCase()]=t})),i}(e,i),o=n.method,h=n.method!==F?{}:r(r({},n.data),i.data),f=r(r(r({"x-algolia-agent":e.userAgent.value},e.queryParameters),h),i.queryParameters),l=0,m=function t(r,a){var h=r.pop();if(void 0===h)throw{name:"RetryError",message:"Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.",transporterStackTrace:w(s)};var m={data:c,headers:u,method:o,url:E(h,n.path,f),connectTimeout:a(l,e.timeouts.connect),responseTimeout:a(l,i.timeout)},d=function(e){var t={request:m,response:e,host:h,triesLeft:r.length};return s.push(t),t},p={onSuccess:function(e){return function(e){try{return JSON.parse(e.content)}catch(t){throw function(e,t){return{name:"DeserializationError",message:e,response:t}}(t.message,e)}}(e)},onRetry:function(n){var i=d(n);return n.isTimedOut&&l++,Promise.all([e.logger.info("Retryable failure",O(i)),e.hostsCache.set(h,y(h,n.isTimedOut?g:v))]).then((function(){return t(r,a)}))},onFail:function(e){throw d(e),function(e,t){var r=e.content,n=e.status,i=r;try{i=JSON.parse(r).message}catch(e){}return function(e,t,r){return{name:"ApiError",message:e,status:t,transporterStackTrace:r}}(i,n,t)}(e,w(s))}};return e.requester.send(m).then((function(e){return function(e,t){return function(e){var t=e.status;return e.isTimedOut||function(e){var t=e.isTimedOut,r=e.status;return!t&&0==~~r}(e)||2!=~~(t/100)&&4!=~~(t/100)}(e)?t.onRetry(e):2==~~(e.status/100)?t.onSuccess(e):t.onFail(e)}(e,p)}))};return P(e.hostsCache,t).then((function(e){return m(a(e.statelessHosts).reverse(),e.getTimeout)}))}function _(e){var t={value:"Algolia for JavaScript (".concat(e,")"),add:function(e){var r="; ".concat(e.segment).concat(void 0!==e.version?" (".concat(e.version,")"):"");return-1===t.value.indexOf(r)&&(t.value="".concat(t.value).concat(r)),t}};return t}function E(e,t,r){var n=x(r),i="".concat(e.protocol,"://").concat(e.url,"/").concat("/"===t.charAt(0)?t.substr(1):t);return n.length&&(i+="?".concat(n)),i}function x(e){return Object.keys(e).map((function(t){return f("%s=%s",t,(r=e[t],"[object Object]"===Object.prototype.toString.call(r)||"[object Array]"===Object.prototype.toString.call(r)?JSON.stringify(e[t]):e[t]));var r})).join("&")}function w(e){return e.map((function(e){return O(e)}))}function O(e){var t=e.request.headers["x-algolia-api-key"]?{"x-algolia-api-key":"*****"}:{};return r(r({},e),{},{request:r(r({},e.request),{},{headers:r(r({},e.request.headers),t)})})}var N=function(e){var t=e.appId,n=function(e,t,r){var n={"x-algolia-api-key":r,"x-algolia-application-id":t};return{headers:function(){return e===l.WithinHeaders?n:{}},queryParameters:function(){return e===l.WithinQueryParameters?n:{}}}}(void 0!==e.authMode?e.authMode:l.WithinHeaders,t,e.apiKey),a=function(e){var t=e.hostsCache,r=e.logger,n=e.requester,a=e.requestsCache,s=e.responsesCache,c=e.timeouts,u=e.userAgent,o=e.hosts,h=e.queryParameters,f={hostsCache:t,logger:r,requester:n,requestsCache:a,responsesCache:s,timeouts:c,userAgent:u,headers:e.headers,queryParameters:h,hosts:o.map((function(e){return R(e)})),read:function(e,t){var r=m(t,f.timeouts.read),n=function(){return j(f,f.hosts.filter((function(e){return 0!=(e.accept&d.Read)})),e,r)};if(!0!==(void 0!==r.cacheable?r.cacheable:e.cacheable))return n();var a={request:e,mappedRequestOptions:r,transporter:{queryParameters:f.queryParameters,headers:f.headers}};return f.responsesCache.get(a,(function(){return f.requestsCache.get(a,(function(){return f.requestsCache.set(a,n()).then((function(e){return Promise.all([f.requestsCache.delete(a),e])}),(function(e){return Promise.all([f.requestsCache.delete(a),Promise.reject(e)])})).then((function(e){var t=i(e,2);return t[0],t[1]}))}))}),{miss:function(e){return f.responsesCache.set(a,e)}})},write:function(e,t){return j(f,f.hosts.filter((function(e){return 0!=(e.accept&d.Write)})),e,m(t,f.timeouts.write))}};return f}(r(r({hosts:[{url:"".concat(t,"-dsn.algolia.net"),accept:d.Read},{url:"".concat(t,".algolia.net"),accept:d.Write}].concat(o([{url:"".concat(t,"-1.algolianet.com")},{url:"".concat(t,"-2.algolianet.com")},{url:"".concat(t,"-3.algolianet.com")}]))},e),{},{headers:r(r(r({},n.headers()),{"content-type":"application/x-www-form-urlencoded"}),e.headers),queryParameters:r(r({},n.queryParameters()),e.queryParameters)}));return h({transporter:a,appId:t,addAlgoliaAgent:function(e,t){a.userAgent.add({segment:e,version:t})},clearCache:function(){return Promise.all([a.requestsCache.clear(),a.responsesCache.clear()]).then((function(){}))}},e.methods)},A=function(e){return function(t,r){return t.method===F?e.transporter.read(t,r):e.transporter.write(t,r)}},H=function(e){return function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return h({transporter:e.transporter,appId:e.appId,indexName:t},r.methods)}},S=function(e){return function(t,n){var i=t.map((function(e){return r(r({},e),{},{params:x(e.params||{})})}));return e.transporter.read({method:b,path:"1/indexes/*/queries",data:{requests:i},cacheable:!0},n)}},T=function(e){return function(t,i){return Promise.all(t.map((function(t){var a=t.params,s=a.facetName,c=a.facetQuery,u=n(a,["facetName","facetQuery"]);return H(e)(t.indexName,{methods:{searchForFacetValues:k}}).searchForFacetValues(s,c,r(r({},i),u))})))}},Q=function(e){return function(t,r,n){return e.transporter.read({method:b,path:f("1/answers/%s/prediction",e.indexName),data:{query:t,queryLanguages:r},cacheable:!0},n)}},C=function(e){return function(t,r){return e.transporter.read({method:b,path:f("1/indexes/%s/query",e.indexName),data:{query:t},cacheable:!0},r)}},k=function(e){return function(t,r,n){return e.transporter.read({method:b,path:f("1/indexes/%s/facets/%s/query",e.indexName,t),data:{facetQuery:r},cacheable:!0},n)}},D=1,I=2,q=3;function V(e,t,n){var i,a={appId:e,apiKey:t,timeouts:{connect:1,read:2,write:30},requester:{send:function(e){return new Promise((function(t){var r=new XMLHttpRequest;r.open(e.method,e.url,!0),Object.keys(e.headers).forEach((function(t){return r.setRequestHeader(t,e.headers[t])}));var n,i=function(e,n){return setTimeout((function(){r.abort(),t({status:0,content:n,isTimedOut:!0})}),1e3*e)},a=i(e.connectTimeout,"Connection timeout");r.onreadystatechange=function(){r.readyState>r.OPENED&&void 0===n&&(clearTimeout(a),n=i(e.responseTimeout,"Socket timeout"))},r.onerror=function(){0===r.status&&(clearTimeout(a),clearTimeout(n),t({content:r.responseText||"Network request failed",status:r.status,isTimedOut:!1}))},r.onload=function(){clearTimeout(a),clearTimeout(n),t({content:r.responseText,status:r.status,isTimedOut:!1})},r.send(e.data)}))}},logger:(i=q,{debug:function(e,t){return D>=i&&console.debug(e,t),Promise.resolve()},info:function(e,t){return I>=i&&console.info(e,t),Promise.resolve()},error:function(e,t){return console.error(e,t),Promise.resolve()}}),responsesCache:u(),requestsCache:u({serializable:!1}),hostsCache:c({caches:[s({key:"".concat("4.14.2","-").concat(e)}),u()]}),userAgent:_("4.14.2").add({segment:"Browser",version:"lite"}),authMode:l.WithinQueryParameters};return N(r(r(r({},a),n),{},{methods:{search:S,searchForFacetValues:T,multipleQueries:S,multipleSearchForFacetValues:T,customRequest:A,initIndex:function(e){return function(t){return H(e)(t,{methods:{search:C,searchForFacetValues:k,findAnswers:Q}})}}}}))}return V.version="4.14.2",V}()},8824:(e,t,r)=>{"use strict";r.d(t,{c:()=>o});var n=r(7294),i=r(2263);const a=["zero","one","two","few","many","other"];function s(e){return a.filter((t=>e.includes(t)))}const c={locale:"en",pluralForms:s(["one","other"]),select:e=>1===e?"one":"other"};function u(){const{i18n:{currentLocale:e}}=(0,i.Z)();return(0,n.useMemo)((()=>{try{return function(e){const t=new Intl.PluralRules(e);return{locale:e,pluralForms:s(t.resolvedOptions().pluralCategories),select:e=>t.select(e)}}(e)}catch(t){return console.error(`Failed to use Intl.PluralRules for locale "${e}".\nDocusaurus will fallback to the default (English) implementation.\nError: ${t.message}\n`),c}}),[e])}function o(){const e=u();return{selectMessage:(t,r)=>function(e,t,r){const n=e.split("|");if(1===n.length)return n[0];n.length>r.pluralForms.length&&console.error(`For locale=${r.locale}, a maximum of ${r.pluralForms.length} plural forms are expected (${r.pluralForms.join(",")}), but the message contains ${n.length}: ${e}`);const i=r.select(t),a=r.pluralForms.indexOf(i);return n[Math.min(a,n.length-1)]}(r,t,e)}}},9172:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>I});var n=r(7294),i=r(6010),a=r(290),s=r.n(a),c=r(8131),u=r.n(c),o=r(5742),h=r(9960),f=r(412),l=r(8824),m=r(8022),d=r(902),p=r(833),v=r(6177),g=r(2128),y=r(2263),R=r(143),F=r(5999),b=r(7767);const P="searchQueryInput_u2C7",j="searchVersionInput_m0Ui",_="searchResultsColumn_JPFH",E="algoliaLogo_rT1R",x="algoliaLogoPathFill_WdUC",w="searchResultItem_Tv2o",O="searchResultItemHeading_KbCB",N="searchResultItemPath_lhe1",A="searchResultItemSummary_AEaO",H="searchQueryColumn_RTkw",S="searchVersionColumn_ypXd",T="searchLogoColumn_rJIA",Q="loadingSpinner_XVxU",C="loader_vvXV";function k(e){let{docsSearchVersionsHelpers:t}=e;const r=Object.entries(t.allDocsData).filter((e=>{let[,t]=e;return t.versions.length>1}));return n.createElement("div",{className:(0,i.Z)("col","col--3","padding-left--none",S)},r.map((e=>{let[i,a]=e;const s=r.length>1?`${i}: `:"";return n.createElement("select",{key:i,onChange:e=>t.setSearchVersion(i,e.target.value),defaultValue:t.searchVersions[i],className:j},a.versions.map(((e,t)=>n.createElement("option",{key:t,label:`${s}${e.label}`,value:e.name}))))})))}function D(){const{siteConfig:{themeConfig:e},i18n:{currentLocale:t}}=(0,y.Z)(),{algolia:{appId:r,apiKey:a,indexName:c,externalUrlRegex:p}}=e,j=function(){const{selectMessage:e}=(0,l.c)();return t=>e(t,(0,F.I)({id:"theme.SearchPage.documentsFound.plurals",description:'Pluralized label for "{count} documents found". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',message:"One document found|{count} documents found"},{count:t}))}(),S=function(){const e=(0,R._r)(),[t,r]=(0,n.useState)((()=>Object.entries(e).reduce(((e,t)=>{let[r,n]=t;return{...e,[r]:n.versions[0].name}}),{}))),i=Object.values(e).some((e=>e.versions.length>1));return{allDocsData:e,versioningEnabled:i,searchVersions:t,setSearchVersion:(e,t)=>r((r=>({...r,[e]:t})))}}(),{searchQuery:D,setSearchQuery:I}=(0,v.O)(),q={items:[],query:null,totalResults:null,totalPages:null,lastPage:null,hasMore:null,loading:null},[V,L]=(0,n.useReducer)(((e,t)=>{switch(t.type){case"reset":return q;case"loading":return{...e,loading:!0};case"update":return D!==t.value.query?e:{...t.value,items:0===t.value.lastPage?t.value.items:e.items.concat(t.value.items)};case"advance":{const t=e.totalPages>e.lastPage+1;return{...e,lastPage:t?e.lastPage+1:e.lastPage,hasMore:t}}default:return e}}),q),B=s()(r,a),z=u()(B,c,{hitsPerPage:15,advancedSyntax:!0,disjunctiveFacets:["language","docusaurus_tag"]});z.on("result",(e=>{let{results:{query:t,hits:r,page:n,nbHits:i,nbPages:a}}=e;if(""===t||!Array.isArray(r))return void L({type:"reset"});const s=e=>e.replace(/algolia-docsearch-suggestion--highlight/g,"search-result-match"),c=r.map((e=>{let{url:t,_highlightResult:{hierarchy:r},_snippetResult:n={}}=e;const i=new URL(t),a=Object.keys(r).map((e=>s(r[e].value)));return{title:a.pop(),url:(0,m.F)(p,i.href)?i.href:i.pathname+i.hash,summary:n.content?`${s(n.content.value)}...`:"",breadcrumbs:a}}));L({type:"update",value:{items:c,query:t,totalResults:i,totalPages:a,lastPage:n,hasMore:a>n+1,loading:!1}})}));const[M,J]=(0,n.useState)(null),U=(0,n.useRef)(0),W=(0,n.useRef)(f.Z.canUseIntersectionObserver&&new IntersectionObserver((e=>{const{isIntersecting:t,boundingClientRect:{y:r}}=e[0];t&&U.current>r&&L({type:"advance"}),U.current=r}),{threshold:1})),Z=()=>D?(0,F.I)({id:"theme.SearchPage.existingResultsTitle",message:'Search results for "{query}"',description:"The search page title for non-empty query"},{query:D}):(0,F.I)({id:"theme.SearchPage.emptyResultsTitle",message:"Search the documentation",description:"The search page title for empty query"}),$=(0,d.zX)((function(e){void 0===e&&(e=0),z.addDisjunctiveFacetRefinement("docusaurus_tag","default"),z.addDisjunctiveFacetRefinement("language",t),Object.entries(S.searchVersions).forEach((e=>{let[t,r]=e;z.addDisjunctiveFacetRefinement("docusaurus_tag",`docs-${t}-${r}`)})),z.setQuery(D).setPage(e).search()}));return(0,n.useEffect)((()=>{if(!M)return;const e=W.current;return e?(e.observe(M),()=>e.unobserve(M)):()=>!0}),[M]),(0,n.useEffect)((()=>{L({type:"reset"}),D&&(L({type:"loading"}),setTimeout((()=>{$()}),300))}),[D,S.searchVersions,$]),(0,n.useEffect)((()=>{V.lastPage&&0!==V.lastPage&&$(V.lastPage)}),[$,V.lastPage]),n.createElement(b.Z,null,n.createElement(o.Z,null,n.createElement("title",null,(0,g.p)(Z())),n.createElement("meta",{property:"robots",content:"noindex, follow"})),n.createElement("div",{className:"container margin-vert--lg"},n.createElement("h1",null,Z()),n.createElement("form",{className:"row",onSubmit:e=>e.preventDefault()},n.createElement("div",{className:(0,i.Z)("col",H,{"col--9":S.versioningEnabled,"col--12":!S.versioningEnabled})},n.createElement("input",{type:"search",name:"q",className:P,placeholder:(0,F.I)({id:"theme.SearchPage.inputPlaceholder",message:"Type your search here",description:"The placeholder for search page input"}),"aria-label":(0,F.I)({id:"theme.SearchPage.inputLabel",message:"Search",description:"The ARIA label for search page input"}),onChange:e=>I(e.target.value),value:D,autoComplete:"off",autoFocus:!0})),S.versioningEnabled&&n.createElement(k,{docsSearchVersionsHelpers:S})),n.createElement("div",{className:"row"},n.createElement("div",{className:(0,i.Z)("col","col--8",_)},!!V.totalResults&&j(V.totalResults)),n.createElement("div",{className:(0,i.Z)("col","col--4","text--right",T)},n.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:"https://www.algolia.com/","aria-label":(0,F.I)({id:"theme.SearchPage.algoliaLabel",message:"Search by Algolia",description:"The ARIA label for Algolia mention"})},n.createElement("svg",{viewBox:"0 0 168 24",className:E},n.createElement("g",{fill:"none"},n.createElement("path",{className:x,d:"M120.925 18.804c-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 0 0-1.574-.199 5.7 5.7 0 0 0-.897.069 2.699 2.699 0 0 0-.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 0 1-1.868.314c-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 0 1-1.471-.636 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525a3.24 3.24 0 0 1 1.047-1.106c.445-.287.95-.492 1.532-.615a8.8 8.8 0 0 1 1.82-.185 8.404 8.404 0 0 1 1.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 0 0-.384-.73 1.784 1.784 0 0 0-.724-.493 3.164 3.164 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 0 0-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 0 1 2.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 0 0-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 0 0-.814.24 1.46 1.46 0 0 0-.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 0 1 .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 0 1-1.471-.635 3.085 3.085 0 0 1-.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 0 1 2.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 0 0-.109-.875 1.873 1.873 0 0 0-.384-.731 1.784 1.784 0 0 0-.724-.492 3.165 3.165 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.75 7.75 0 0 0-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 0 1 2.073-.177zm-8.034-1.271a1.626 1.626 0 0 1-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 0 1-1.128 1.906 4.986 4.986 0 0 1-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 0 1-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 0 1-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 0 1 1.15-1.892 5.133 5.133 0 0 1 1.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423a4.88 4.88 0 0 1 1.753 1.216 5.644 5.644 0 0 1 1.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 0 0-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 0 1-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 0 1-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 0 1 2.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17zM6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 0 0-.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 0 1-.582-.271 13.67 13.67 0 0 1-.55-.287 4.275 4.275 0 0 1-.567-.351 6.92 6.92 0 0 1-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 0 1-.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 0 0-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 0 0-.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 0 0-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 0 1-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z"}),n.createElement("path",{fill:"#5468FF",d:"M78.988.938h16.594a2.968 2.968 0 0 1 2.966 2.966V20.5a2.967 2.967 0 0 1-2.966 2.964H78.988a2.967 2.967 0 0 1-2.966-2.964V3.897A2.961 2.961 0 0 1 78.988.938z"}),n.createElement("path",{fill:"white",d:"M89.632 5.967v-.772a.978.978 0 0 0-.978-.977h-2.28a.978.978 0 0 0-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 0 1 1.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 0 0-1.382 0l-.465.465a.973.973 0 0 0 0 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 0 0-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 0 1-4.49-4.482 4.488 4.488 0 0 1 4.49-4.482 4.488 4.488 0 0 1 4.489 4.482 4.484 4.484 0 0 1-4.49 4.482m0-10.85a6.363 6.363 0 1 0 0 12.729 6.37 6.37 0 0 0 6.372-6.368 6.358 6.358 0 0 0-6.371-6.36"})))))),V.items.length>0?n.createElement("main",null,V.items.map(((e,t)=>{let{title:r,url:a,summary:s,breadcrumbs:c}=e;return n.createElement("article",{key:t,className:w},n.createElement("h2",{className:O},n.createElement(h.Z,{to:a,dangerouslySetInnerHTML:{__html:r}})),c.length>0&&n.createElement("nav",{"aria-label":"breadcrumbs"},n.createElement("ul",{className:(0,i.Z)("breadcrumbs",N)},c.map(((e,t)=>n.createElement("li",{key:t,className:"breadcrumbs__item",dangerouslySetInnerHTML:{__html:e}}))))),s&&n.createElement("p",{className:A,dangerouslySetInnerHTML:{__html:s}}))}))):[D&&!V.loading&&n.createElement("p",{key:"no-results"},n.createElement(F.Z,{id:"theme.SearchPage.noResultsText",description:"The paragraph for empty search result"},"No results were found")),!!V.loading&&n.createElement("div",{key:"spinner",className:Q})],V.hasMore&&n.createElement("div",{className:C,ref:J},n.createElement(F.Z,{id:"theme.SearchPage.fetchingNewResults",description:"The paragraph for fetching new search results"},"Fetching new results..."))))}function I(){return n.createElement(p.FG,{className:"search-page-wrapper"},n.createElement(D,null))}}}]); \ No newline at end of file diff --git a/assets/js/1a4e3797.66dfb1ef.js.LICENSE.txt b/assets/js/1a4e3797.66dfb1ef.js.LICENSE.txt new file mode 100644 index 0000000..1d9697b --- /dev/null +++ b/assets/js/1a4e3797.66dfb1ef.js.LICENSE.txt @@ -0,0 +1 @@ +/*! algoliasearch-lite.umd.js | 4.14.2 | © Algolia, inc. | https://github.com/algolia/algoliasearch-client-javascript */ diff --git a/assets/js/1aa4b20d.99646ca2.js b/assets/js/1aa4b20d.99646ca2.js new file mode 100644 index 0000000..aa3bb34 --- /dev/null +++ b/assets/js/1aa4b20d.99646ca2.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[1911],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>d});var n=r(7294);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function s(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var c=n.createContext({}),l=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},u=function(e){var t=l(e.components);return n.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,o=e.originalType,c=e.parentName,u=a(e,["components","mdxType","originalType","parentName"]),f=l(r),d=i,m=f["".concat(c,".").concat(d)]||f[d]||p[d]||o;return r?n.createElement(m,s(s({ref:t},u),{},{components:r})):n.createElement(m,s({ref:t},u))}));function d(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=r.length,s=new Array(o);s[0]=f;var a={};for(var c in t)hasOwnProperty.call(t,c)&&(a[c]=t[c]);a.originalType=e,a.mdxType="string"==typeof e?e:i,s[1]=a;for(var l=2;l{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>p,frontMatter:()=>o,metadata:()=>a,toc:()=>l});var n=r(7462),i=(r(7294),r(3905));const o={},s="k9s",a={unversionedId:"general/utilities/k9s",id:"general/utilities/k9s",title:"k9s",description:"Makes your life with k8s so much better. Here are the typical use cases.",source:"@site/docs/general/utilities/k9s.md",sourceDirName:"general/utilities",slug:"/general/utilities/k9s",permalink:"/docs/general/utilities/k9s",draft:!1,editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/docs/general/utilities/k9s.md",tags:[],version:"current",frontMatter:{},sidebar:"generalSidebar",previous:{title:"Security",permalink:"/docs/general/security"}},c={},l=[{value:"inspecting k8s",id:"inspecting-k8s",level:2},{value:"restarting pods",id:"restarting-pods",level:2},{value:"editing resources",id:"editing-resources",level:2}],u={toc:l};function p(e){let{components:t,...r}=e;return(0,i.kt)("wrapper",(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("h1",{id:"k9s"},"k9s"),(0,i.kt)("p",null,"Makes your life with k8s so much better. Here are the typical use cases."),(0,i.kt)("h2",{id:"inspecting-k8s"},"inspecting k8s"),(0,i.kt)("h2",{id:"restarting-pods"},"restarting pods"),(0,i.kt)("h2",{id:"editing-resources"},"editing resources"))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/1aa4b20d.bc141a6f.js b/assets/js/1aa4b20d.bc141a6f.js deleted file mode 100644 index 2b36a80..0000000 --- a/assets/js/1aa4b20d.bc141a6f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[1911],{3905:function(e,t,r){r.d(t,{Zo:function(){return l},kt:function(){return d}});var n=r(7294);function i(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function c(e){for(var t=1;t=0||(i[r]=e[r]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(i[r]=e[r])}return i}var a=n.createContext({}),u=function(e){var t=n.useContext(a),r=t;return e&&(r="function"==typeof e?e(t):c(c({},t),e)),r},l=function(e){var t=u(e.components);return n.createElement(a.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,i=e.mdxType,o=e.originalType,a=e.parentName,l=s(e,["components","mdxType","originalType","parentName"]),f=u(r),d=i,m=f["".concat(a,".").concat(d)]||f[d]||p[d]||o;return r?n.createElement(m,c(c({ref:t},l),{},{components:r})):n.createElement(m,c({ref:t},l))}));function d(e,t){var r=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=r.length,c=new Array(o);c[0]=f;var s={};for(var a in t)hasOwnProperty.call(t,a)&&(s[a]=t[a]);s.originalType=e,s.mdxType="string"==typeof e?e:i,c[1]=s;for(var u=2;u=0||(a[n]=e[n]);return a}(e,r);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(t=0;t=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var l=t.createContext({}),s=function(e){var r=t.useContext(l),n=r;return e&&(n="function"==typeof e?e(r):c(c({},r),e)),n},u=function(e){var r=s(e.components);return t.createElement(l.Provider,{value:r},e.children)},p={inlineCode:"code",wrapper:function(e){var r=e.children;return t.createElement(t.Fragment,{},r)}},m=t.forwardRef((function(e,r){var n=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),m=s(n),f=a,g=m["".concat(l,".").concat(f)]||m[f]||p[f]||o;return n?t.createElement(g,c(c({ref:r},u),{},{components:n})):t.createElement(g,c({ref:r},u))}));function f(e,r){var n=arguments,a=r&&r.mdxType;if("string"==typeof e||a){var o=n.length,c=new Array(o);c[0]=m;var i={};for(var l in r)hasOwnProperty.call(r,l)&&(i[l]=r[l]);i.originalType=e,i.mdxType="string"==typeof e?e:a,c[1]=i;for(var s=2;s{t.d(r,{Zo:()=>p,kt:()=>g});var n=t(7294);function a(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function o(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,n)}return t}function c(e){for(var r=1;r=0||(a[t]=e[t]);return a}(e,r);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var l=n.createContext({}),s=function(e){var r=n.useContext(l),t=r;return e&&(t="function"==typeof e?e(r):c(c({},r),e)),t},p=function(e){var r=s(e.components);return n.createElement(l.Provider,{value:r},e.children)},u={inlineCode:"code",wrapper:function(e){var r=e.children;return n.createElement(n.Fragment,{},r)}},m=n.forwardRef((function(e,r){var t=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),m=s(t),g=a,d=m["".concat(l,".").concat(g)]||m[g]||u[g]||o;return t?n.createElement(d,c(c({ref:r},p),{},{components:t})):n.createElement(d,c({ref:r},p))}));function g(e,r){var t=arguments,a=r&&r.mdxType;if("string"==typeof e||a){var o=t.length,c=new Array(o);c[0]=m;var i={};for(var l in r)hasOwnProperty.call(r,l)&&(i[l]=r[l]);i.originalType=e,i.mdxType="string"==typeof e?e:a,c[1]=i;for(var s=2;s{t.r(r),t.d(r,{assets:()=>l,contentTitle:()=>c,default:()=>u,frontMatter:()=>o,metadata:()=>i,toc:()=>s});var n=t(7462),a=(t(7294),t(3905));const o={id:"package-managers",sidebar_label:"Package managers",sidebar_position:4},c="Package managers",i={unversionedId:"general/package-managers",id:"general/package-managers",title:"Package managers",description:"Whenever you can, do not manage software on your computer manually ...",source:"@site/docs/general/package-managers.md",sourceDirName:"general",slug:"/general/package-managers",permalink:"/docs/general/package-managers",draft:!1,editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/docs/general/package-managers.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{id:"package-managers",sidebar_label:"Package managers",sidebar_position:4},sidebar:"generalSidebar",previous:{title:"Working remotely",permalink:"/docs/general/work/remote-work"},next:{title:"SSE Server Sent Events",permalink:"/docs/general/technologies/sse"}},l={},s=[{value:"macOS",id:"macos",level:2},{value:"Windows",id:"windows",level:2}],p={toc:s};function u(e){let{components:r,...t}=e;return(0,a.kt)("wrapper",(0,n.Z)({},p,t,{components:r,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"package-managers"},"Package managers"),(0,a.kt)("p",null,"Whenever you can, do not manage software on your computer manually ..."),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"comfort"),(0,a.kt)("li",{parentName:"ul"},"versions"),(0,a.kt)("li",{parentName:"ul"},"security")),(0,a.kt)("h2",{id:"macos"},"macOS"),(0,a.kt)("p",null,"On the mac ",(0,a.kt)("a",{parentName:"p",href:"https://brew.sh/"},"https://brew.sh/")," has become the de facto standard - we use it to distribute binaries of some of our open source projects as well."),(0,a.kt)("h2",{id:"windows"},"Windows"),(0,a.kt)("p",null,"..."))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/1be78505.6726295f.js b/assets/js/1be78505.6726295f.js deleted file mode 100644 index 8b76502..0000000 --- a/assets/js/1be78505.6726295f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[9514,4608],{5642:function(e,t,a){a.r(t),a.d(t,{default:function(){return ee}});var n=a(7294),l=a(3905),o=a(6291),r=a(8882),c=a(6010),i=a(6681),s=a(3783),d=a(5537),m=a(3117);var u=function(e){return n.createElement("svg",(0,m.Z)({width:"20",height:"20","aria-hidden":"true"},e),n.createElement("g",{fill:"#7a7a7a"},n.createElement("path",{d:"M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"}),n.createElement("path",{d:"M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"})))},b=a(5999),p=a(102),h=a(9960),f=a(3919),v=a(541),E="menuLinkText_OKON",g="hasHref_TwRn",k=a(2389),_=["items"],C=["item"],N=["item","onItemClick","activePath","level"],Z=["item","onItemClick","activePath","level"],S=(0,n.memo)((function(e){var t=e.items,a=(0,p.Z)(e,_);return n.createElement(n.Fragment,null,t.map((function(e,t){return n.createElement(I,(0,m.Z)({key:t,item:e},a))})))}));function I(e){var t=e.item,a=(0,p.Z)(e,C);return"category"===t.type?0===t.items.length?null:n.createElement(T,(0,m.Z)({item:t},a)):n.createElement(M,(0,m.Z)({item:t},a))}function T(e){var t,a=e.item,l=e.onItemClick,o=e.activePath,r=e.level,s=(0,p.Z)(e,N),d=a.items,u=a.label,f=a.collapsible,v=a.className,_=a.href,C=function(e){var t=(0,k.Z)();return(0,n.useMemo)((function(){return e.href?e.href:!t&&e.collapsible?(0,i.Wl)(e):void 0}),[e,t])}(a),Z=(0,i._F)(a,o),I=(0,i.uR)({initialState:function(){return!!f&&(!Z&&a.collapsed)}}),T=I.collapsed,M=I.setCollapsed,y=I.toggleCollapsed;return function(e){var t=e.isActive,a=e.collapsed,l=e.setCollapsed,o=(0,i.D9)(t);(0,n.useEffect)((function(){t&&!o&&a&&l(!1)}),[t,o,a,l])}({isActive:Z,collapsed:T,setCollapsed:M}),n.createElement("li",{className:(0,c.Z)(i.kM.docs.docSidebarItemCategory,i.kM.docs.docSidebarItemCategoryLevel(r),"menu__list-item",{"menu__list-item--collapsed":T},v)},n.createElement("div",{className:"menu__list-item-collapsible"},n.createElement(h.Z,(0,m.Z)({className:(0,c.Z)("menu__link",(t={"menu__link--sublist":f&&!_,"menu__link--active":Z},t[E]=!f,t[g]=!!C,t)),onClick:f?function(e){null==l||l(a),_?M(!1):(e.preventDefault(),y())}:function(){null==l||l(a)},href:f?null!=C?C:"#":C},s),u),_&&f&&n.createElement("button",{"aria-label":(0,b.I)({id:"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel",message:"Toggle the collapsible sidebar category '{label}'",description:"The ARIA label to toggle the collapsible sidebar category"},{label:u}),type:"button",className:"clean-btn menu__caret",onClick:function(e){e.preventDefault(),y()}})),n.createElement(i.zF,{lazy:!0,as:"ul",className:"menu__list",collapsed:T},n.createElement(S,{items:d,tabIndex:T?-1:0,onItemClick:l,activePath:o,level:r+1})))}function M(e){var t=e.item,a=e.onItemClick,l=e.activePath,o=e.level,r=(0,p.Z)(e,Z),s=t.href,d=t.label,u=t.className,b=(0,i._F)(t,l);return n.createElement("li",{className:(0,c.Z)(i.kM.docs.docSidebarItemLink,i.kM.docs.docSidebarItemLinkLevel(o),"menu__list-item",u),key:d},n.createElement(h.Z,(0,m.Z)({className:(0,c.Z)("menu__link",{"menu__link--active":b}),"aria-current":b?"page":void 0,to:s},(0,f.Z)(s)&&{onClick:a?function(){return a(t)}:void 0},r),(0,f.Z)(s)?d:n.createElement("span",null,d,n.createElement(v.Z,null))))}var y="sidebar_a3j0",A="sidebarWithHideableNavbar_VlPv",w="sidebarHidden_OqfG",F="sidebarLogo_hmkv",L="menu_cyFh",B="menuWithAnnouncementBar_+O1J",P="collapseSidebarButton_eoK2",x="collapseSidebarButtonIcon_e+kA";function R(e){var t=e.onClick;return n.createElement("button",{type:"button",title:(0,b.I)({id:"theme.docs.sidebar.collapseButtonTitle",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),"aria-label":(0,b.I)({id:"theme.docs.sidebar.collapseButtonAriaLabel",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),className:(0,c.Z)("button button--secondary button--outline",P),onClick:t},n.createElement(u,{className:x}))}function D(e){var t,a,l=e.path,o=e.sidebar,r=e.onCollapse,s=e.isHidden,m=function(){var e=(0,i.nT)().isActive,t=(0,n.useState)(e),a=t[0],l=t[1];return(0,i.RF)((function(t){var a=t.scrollY;e&&l(0===a)}),[e]),e&&a}(),u=(0,i.LU)(),b=u.navbar.hideOnScroll,p=u.hideableSidebar;return n.createElement("div",{className:(0,c.Z)(y,(t={},t[A]=b,t[w]=s,t))},b&&n.createElement(d.Z,{tabIndex:-1,className:F}),n.createElement("nav",{className:(0,c.Z)("menu thin-scrollbar",L,(a={},a[B]=m,a))},n.createElement("ul",{className:(0,c.Z)(i.kM.docs.docSidebarMenu,"menu__list")},n.createElement(S,{items:o,activePath:l,level:1}))),p&&n.createElement(R,{onClick:r}))}var H=function(e){var t=e.toggleSidebar,a=e.sidebar,l=e.path;return n.createElement("ul",{className:(0,c.Z)(i.kM.docs.docSidebarMenu,"menu__list")},n.createElement(S,{items:a,activePath:l,onItemClick:function(e){"category"===e.type&&e.href&&t(),"link"===e.type&&t()},level:1}))};function W(e){return n.createElement(i.Cv,{component:H,props:e})}var O=n.memo(D),q=n.memo(W);function z(e){var t=(0,s.Z)(),a="desktop"===t||"ssr"===t,l="mobile"===t;return n.createElement(n.Fragment,null,a&&n.createElement(O,e),l&&n.createElement(q,e))}var Y=a(9014),K=a(4608),U="backToTopButton_i9tI",V="backToTopButtonShow_wCmF";function j(){var e=(0,n.useRef)(null);return{smoothScrollTop:function(){var t;e.current=(t=null,function e(){var a=document.documentElement.scrollTop;a>0&&(t=requestAnimationFrame(e),window.scrollTo(0,Math.floor(.85*a)))}(),function(){return t&&cancelAnimationFrame(t)})},cancelScrollToTop:function(){return null==e.current?void 0:e.current()}}}var G=function(){var e,t=(0,n.useState)(!1),a=t[0],l=t[1],o=(0,n.useRef)(!1),r=j(),s=r.smoothScrollTop,d=r.cancelScrollToTop;return(0,i.RF)((function(e,t){var a=e.scrollY,n=null==t?void 0:t.scrollY;if(n)if(o.current)o.current=!1;else{var r=a{n.r(t),n.d(t,{default:()=>Ie});var a=n(7294),l=n(6010),o=n(833),r=n(5281),c=n(3320),i=n(2802),s=n(4477),d=n(1116),m=n(7767),u=n(5999),b=n(2466),p=n(5936);const h="backToTopButton_sjWU",E="backToTopButtonShow_xfvO";function f(){const{shown:e,scrollToTop:t}=function(e){let{threshold:t}=e;const[n,l]=(0,a.useState)(!1),o=(0,a.useRef)(!1),{startScroll:r,cancelScroll:c}=(0,b.Ct)();return(0,b.RF)(((e,n)=>{let{scrollY:a}=e;const r=null==n?void 0:n.scrollY;r&&(o.current?o.current=!1:a>=r?(c(),l(!1)):a{e.location.hash&&(o.current=!0,l(!1))})),{shown:n,scrollToTop:()=>r(0)}}({threshold:300});return a.createElement("button",{"aria-label":(0,u.I)({id:"theme.BackToTopButton.buttonAriaLabel",message:"Scroll back to top",description:"The ARIA label for the back to top button"}),className:(0,l.Z)("clean-btn",r.k.common.backToTopButton,h,e&&E),type:"button",onClick:t})}var v=n(6775),g=n(7524),_=n(6668),k=n(1327),C=n(7462);function I(e){return a.createElement("svg",(0,C.Z)({width:"20",height:"20","aria-hidden":"true"},e),a.createElement("g",{fill:"#7a7a7a"},a.createElement("path",{d:"M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"}),a.createElement("path",{d:"M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"})))}const N="collapseSidebarButton_PEFL",S="collapseSidebarButtonIcon_kv0_";function Z(e){let{onClick:t}=e;return a.createElement("button",{type:"button",title:(0,u.I)({id:"theme.docs.sidebar.collapseButtonTitle",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),"aria-label":(0,u.I)({id:"theme.docs.sidebar.collapseButtonAriaLabel",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),className:(0,l.Z)("button button--secondary button--outline",N),onClick:t},a.createElement(I,{className:S}))}var x=n(9689),y=n(902);const T=Symbol("EmptyContext"),w=a.createContext(T);function L(e){let{children:t}=e;const[n,l]=(0,a.useState)(null),o=(0,a.useMemo)((()=>({expandedItem:n,setExpandedItem:l})),[n]);return a.createElement(w.Provider,{value:o},t)}var M=n(6043),A=n(8596),B=n(9960),F=n(2389);function P(e){let{categoryLabel:t,onClick:n}=e;return a.createElement("button",{"aria-label":(0,u.I)({id:"theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel",message:"Toggle the collapsible sidebar category '{label}'",description:"The ARIA label to toggle the collapsible sidebar category"},{label:t}),type:"button",className:"clean-btn menu__caret",onClick:n})}function H(e){let{item:t,onItemClick:n,activePath:o,level:c,index:s,...d}=e;const{items:m,label:u,collapsible:b,className:p,href:h}=t,{docs:{sidebar:{autoCollapseCategories:E}}}=(0,_.L)(),f=function(e){const t=(0,F.Z)();return(0,a.useMemo)((()=>e.href?e.href:!t&&e.collapsible?(0,i.Wl)(e):void 0),[e,t])}(t),v=(0,i._F)(t,o),g=(0,A.Mg)(h,o),{collapsed:k,setCollapsed:I}=(0,M.u)({initialState:()=>!!b&&(!v&&t.collapsed)}),{expandedItem:N,setExpandedItem:S}=function(){const e=(0,a.useContext)(w);if(e===T)throw new y.i6("DocSidebarItemsExpandedStateProvider");return e}(),Z=function(e){void 0===e&&(e=!k),S(e?null:s),I(e)};return function(e){let{isActive:t,collapsed:n,updateCollapsed:l}=e;const o=(0,y.D9)(t);(0,a.useEffect)((()=>{t&&!o&&n&&l(!1)}),[t,o,n,l])}({isActive:v,collapsed:k,updateCollapsed:Z}),(0,a.useEffect)((()=>{b&&null!=N&&N!==s&&E&&I(!0)}),[b,N,s,I,E]),a.createElement("li",{className:(0,l.Z)(r.k.docs.docSidebarItemCategory,r.k.docs.docSidebarItemCategoryLevel(c),"menu__list-item",{"menu__list-item--collapsed":k},p)},a.createElement("div",{className:(0,l.Z)("menu__list-item-collapsible",{"menu__list-item-collapsible--active":g})},a.createElement(B.Z,(0,C.Z)({className:(0,l.Z)("menu__link",{"menu__link--sublist":b,"menu__link--sublist-caret":!h&&b,"menu__link--active":v}),onClick:b?e=>{null==n||n(t),h?Z(!1):(e.preventDefault(),Z())}:()=>{null==n||n(t)},"aria-current":g?"page":void 0,"aria-expanded":b?!k:void 0,href:b?f??"#":f},d),u),h&&b&&a.createElement(P,{categoryLabel:u,onClick:e=>{e.preventDefault(),Z()}})),a.createElement(M.z,{lazy:!0,as:"ul",className:"menu__list",collapsed:k},a.createElement(q,{items:m,tabIndex:k?-1:0,onItemClick:n,activePath:o,level:c+1})))}var W=n(3919),D=n(9471);const R="menuExternalLink_NmtK";function z(e){let{item:t,onItemClick:n,activePath:o,level:c,index:s,...d}=e;const{href:m,label:u,className:b,autoAddBaseUrl:p}=t,h=(0,i._F)(t,o),E=(0,W.Z)(m);return a.createElement("li",{className:(0,l.Z)(r.k.docs.docSidebarItemLink,r.k.docs.docSidebarItemLinkLevel(c),"menu__list-item",b),key:u},a.createElement(B.Z,(0,C.Z)({className:(0,l.Z)("menu__link",!E&&R,{"menu__link--active":h}),autoAddBaseUrl:p,"aria-current":h?"page":void 0,to:m},E&&{onClick:n?()=>n(t):void 0},d),u,!E&&a.createElement(D.Z,null)))}const U="menuHtmlItem_M9Kj";function V(e){let{item:t,level:n,index:o}=e;const{value:c,defaultStyle:i,className:s}=t;return a.createElement("li",{className:(0,l.Z)(r.k.docs.docSidebarItemLink,r.k.docs.docSidebarItemLinkLevel(n),i&&[U,"menu__list-item"],s),key:o,dangerouslySetInnerHTML:{__html:c}})}function K(e){let{item:t,...n}=e;switch(t.type){case"category":return a.createElement(H,(0,C.Z)({item:t},n));case"html":return a.createElement(V,(0,C.Z)({item:t},n));default:return a.createElement(z,(0,C.Z)({item:t},n))}}function j(e){let{items:t,...n}=e;return a.createElement(L,null,t.map(((e,t)=>a.createElement(K,(0,C.Z)({key:t,item:e,index:t},n)))))}const q=(0,a.memo)(j),G="menu_SIkG",Y="menuWithAnnouncementBar_GW3s";function O(e){let{path:t,sidebar:n,className:o}=e;const c=function(){const{isActive:e}=(0,x.nT)(),[t,n]=(0,a.useState)(e);return(0,b.RF)((t=>{let{scrollY:a}=t;e&&n(0===a)}),[e]),e&&t}();return a.createElement("nav",{className:(0,l.Z)("menu thin-scrollbar",G,c&&Y,o)},a.createElement("ul",{className:(0,l.Z)(r.k.docs.docSidebarMenu,"menu__list")},a.createElement(q,{items:n,activePath:t,level:1})))}const X="sidebar_njMd",J="sidebarWithHideableNavbar_wUlq",Q="sidebarHidden_VK0M",$="sidebarLogo_isFc";function ee(e){let{path:t,sidebar:n,onCollapse:o,isHidden:r}=e;const{navbar:{hideOnScroll:c},docs:{sidebar:{hideable:i}}}=(0,_.L)();return a.createElement("div",{className:(0,l.Z)(X,c&&J,r&&Q)},c&&a.createElement(k.Z,{tabIndex:-1,className:$}),a.createElement(O,{path:t,sidebar:n}),i&&a.createElement(Z,{onClick:o}))}const te=a.memo(ee);var ne=n(3102),ae=n(2961);const le=e=>{let{sidebar:t,path:n}=e;const o=(0,ae.e)();return a.createElement("ul",{className:(0,l.Z)(r.k.docs.docSidebarMenu,"menu__list")},a.createElement(q,{items:t,activePath:n,onItemClick:e=>{"category"===e.type&&e.href&&o.toggle(),"link"===e.type&&o.toggle()},level:1}))};function oe(e){return a.createElement(ne.Zo,{component:le,props:e})}const re=a.memo(oe);function ce(e){const t=(0,g.i)(),n="desktop"===t||"ssr"===t,l="mobile"===t;return a.createElement(a.Fragment,null,n&&a.createElement(te,e),l&&a.createElement(re,e))}const ie="expandButton_m80_",se="expandButtonIcon_BlDH";function de(e){let{toggleSidebar:t}=e;return a.createElement("div",{className:ie,title:(0,u.I)({id:"theme.docs.sidebar.expandButtonTitle",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),"aria-label":(0,u.I)({id:"theme.docs.sidebar.expandButtonAriaLabel",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),tabIndex:0,role:"button",onKeyDown:t,onClick:t},a.createElement(I,{className:se}))}const me="docSidebarContainer_b6E3",ue="docSidebarContainerHidden_b3ry";function be(e){let{children:t}=e;const n=(0,d.V)();return a.createElement(a.Fragment,{key:(null==n?void 0:n.name)??"noSidebar"},t)}function pe(e){let{sidebar:t,hiddenSidebarContainer:n,setHiddenSidebarContainer:o}=e;const{pathname:c}=(0,v.TH)(),[i,s]=(0,a.useState)(!1),d=(0,a.useCallback)((()=>{i&&s(!1),o((e=>!e))}),[o,i]);return a.createElement("aside",{className:(0,l.Z)(r.k.docs.docSidebarContainer,me,n&&ue),onTransitionEnd:e=>{e.currentTarget.classList.contains(me)&&n&&s(!0)}},a.createElement(be,null,a.createElement(ce,{sidebar:t,path:c,onCollapse:d,isHidden:i})),i&&a.createElement(de,{toggleSidebar:d}))}const he={docMainContainer:"docMainContainer_gTbr",docMainContainerEnhanced:"docMainContainerEnhanced_Uz_u",docItemWrapperEnhanced:"docItemWrapperEnhanced_czyv"};function Ee(e){let{hiddenSidebarContainer:t,children:n}=e;const o=(0,d.V)();return a.createElement("main",{className:(0,l.Z)(he.docMainContainer,(t||!o)&&he.docMainContainerEnhanced)},a.createElement("div",{className:(0,l.Z)("container padding-top--md padding-bottom--lg",he.docItemWrapper,t&&he.docItemWrapperEnhanced)},n))}const fe="docPage__5DB",ve="docsWrapper_BCFX";function ge(e){let{children:t}=e;const n=(0,d.V)(),[l,o]=(0,a.useState)(!1);return a.createElement(m.Z,{wrapperClassName:ve},a.createElement(f,null),a.createElement("div",{className:fe},n&&a.createElement(pe,{sidebar:n.items,hiddenSidebarContainer:l,setHiddenSidebarContainer:o}),a.createElement(Ee,{hiddenSidebarContainer:l},t)))}var _e=n(4972),ke=n(197);function Ce(e){const{versionMetadata:t}=e;return a.createElement(a.Fragment,null,a.createElement(ke.Z,{version:t.version,tag:(0,c.os)(t.pluginId,t.version)}),a.createElement(o.d,null,t.noIndex&&a.createElement("meta",{name:"robots",content:"noindex, nofollow"})))}function Ie(e){const{versionMetadata:t}=e,n=(0,i.hI)(e);if(!n)return a.createElement(_e.default,null);const{docElement:c,sidebarName:m,sidebarItems:u}=n;return a.createElement(a.Fragment,null,a.createElement(Ce,e),a.createElement(o.FG,{className:(0,l.Z)(r.k.wrapper.docsPages,r.k.page.docsDocPage,e.versionMetadata.className)},a.createElement(s.q,{version:t},a.createElement(d.b,{name:m,items:u},a.createElement(ge,null,c)))))}},4972:(e,t,n)=>{n.r(t),n.d(t,{default:()=>c});var a=n(7294),l=n(5999),o=n(833),r=n(7767);function c(){return a.createElement(a.Fragment,null,a.createElement(o.d,{title:(0,l.I)({id:"theme.NotFound.title",message:"Page Not Found"})}),a.createElement(r.Z,null,a.createElement("main",{className:"container margin-vert--xl"},a.createElement("div",{className:"row"},a.createElement("div",{className:"col col--6 col--offset-3"},a.createElement("h1",{className:"hero__title"},a.createElement(l.Z,{id:"theme.NotFound.title",description:"The title of the 404 page"},"Page Not Found")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page"},"We could not find what you were looking for.")),a.createElement("p",null,a.createElement(l.Z,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page"},"Please contact the owner of the site that linked you to the original URL and let them know their link is broken.")))))))}},4477:(e,t,n)=>{n.d(t,{E:()=>c,q:()=>r});var a=n(7294),l=n(902);const o=a.createContext(null);function r(e){let{children:t,version:n}=e;return a.createElement(o.Provider,{value:n},t)}function c(){const e=(0,a.useContext)(o);if(null===e)throw new l.i6("DocsVersionProvider");return e}}}]); \ No newline at end of file diff --git a/assets/js/1df93b7f.108ac62c.js b/assets/js/1df93b7f.108ac62c.js deleted file mode 100644 index d61ea2a..0000000 --- a/assets/js/1df93b7f.108ac62c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[3237],{8368:function(e,t,n){n.r(t),n.d(t,{default:function(){return w}});var r=n(7294),l=n(6010),o=n(8882),a=n(2263),i="heroBanner_etFc",c=n(3117),s=n(9960),m="features_n4mZ",u="feature_TYIJ",d="featureSvg_d3xR",h=function(e){return r.createElement(s.Z,{className:"button button--secondary",to:e.to},e.children)},p=[{title:"General guides",description:r.createElement(r.Fragment,null,"no matter if frontend or backend - there is somtehing for everyone in here"),callToAction:"get started",to:"/docs/general/intro"},{title:"Frontend guides",description:r.createElement(r.Fragment,null,"Build frontends with TypeScript, Next.js, styled components, gotsrpc"),callToAction:"go build a frontend",to:"/docs/frontend/intro"},{title:"Backend guides",description:r.createElement(r.Fragment,null,"Write fast and reliable services in Go and run them in the cloud"),callToAction:"build a server",to:"/docs/backend/intro"},{title:"Projects",description:r.createElement(r.Fragment,null,"Foomo projects - libraries, utilities, friendly daemons and more"),callToAction:"explore",to:"/docs/projects/intro"},{title:"CMS",description:r.createElement(r.Fragment,null,"foomo has extensive support for headless CMS Systems"),callToAction:"learn the patterns",to:"/docs/projects/cms/intro"},{title:"Architecture",description:r.createElement(r.Fragment,null,"Show me the big picture - how does foomo integrate into the k8s and Next.js eco system"),callToAction:"explore the diagrams",to:"/docs/projects/architecture"},{title:"Awesome Software",description:r.createElement(r.Fragment,null,"a curated list of software we use in the cloud, on our desktops and our devices"),callToAction:"show me the list",to:"/awesome-software"},{title:"DevOps",description:r.createElement(r.Fragment,null,"our approach to run cloud native applications where they belong"),callToAction:"take me to the cloud",to:"/docs/devops/intro"},{title:"Blog",description:r.createElement(r.Fragment,null,"random information about programming, our ecosystem and misc fun topics"),callToAction:"read the blog",to:"/blog"}];function g(e){var t=e.title,n=e.image,l=e.description,o=e.to,a=e.callToAction;return r.createElement("div",{className:"col col--4 "},r.createElement("div",{className:u},r.createElement("div",{className:"text--center"},n&&r.createElement("img",{className:d,alt:t,src:n})),r.createElement("div",{className:"text--center padding-horiz--md"},r.createElement("h3",null,t),r.createElement("p",null,l)),r.createElement("div",{className:"text--center"},r.createElement(h,{to:o},a))))}function f(){return r.createElement("section",{className:m},r.createElement("div",{className:"container"},r.createElement("div",{className:"row"},p.map((function(e,t){return r.createElement(g,(0,c.Z)({key:t},e))})))))}var E=function(){var e=(0,a.Z)().siteConfig;return r.createElement("header",{className:(0,l.Z)("hero hero--primary",i)},r.createElement("div",{className:"container"},r.createElement("img",{style:{width:"200px",height:"200px",backgroundColor:"black",border:"1px white solid",borderRadius:"1rem"},src:"https://avatars.githubusercontent.com/u/889755"}),r.createElement("h1",{className:"hero__title"},e.title),r.createElement("p",{className:"hero__subtitle"},r.createElement("q",null,e.tagline))))},b=function(e){return r.createElement("span",{style:{fontWeight:"bold",backgroundColor:"#ffea00",border:"none",padding:"1px 4px",textShadow:"1px 1px 2px #00000081"},title:e.href},r.createElement("a",{href:e.href,style:{color:"white"}},e.children))};function w(){var e=(0,a.Z)().siteConfig;return r.createElement(o.Z,{title:"Hello from "+e.title,description:"Description will go into a meta tag in "},r.createElement(E,null),r.createElement("div",{className:"container",style:{maxWidth:"640px",marginLeft:"auto",marginRight:"auto"}},r.createElement("br",null),r.createElement("p",null,r.createElement("b",null,"foomo")," is an open source project, that has been maintained by the"," ",r.createElement(b,{href:"https://www.bestbytes.com"},"bestbytes")," ","team since 2011. It provides a wide range of utilities, libraries and daemons, that help us to tackle challenging projects."),r.createElement("h2",null,"foomo can help, if you are"),r.createElement("ul",null,r.createElement("li",null,"using"," ",r.createElement(b,{href:"https://www.golang.org"},"Go")," to write services"),r.createElement("li",null,"building frontends with"," ",r.createElement(b,{href:"https://www.typescriptlang.org/"},"TypeScript")," ","and"," ",r.createElement(b,{href:"https://nextjs.org/"},"Next.js")),r.createElement("li",null,"running your software on"," ",r.createElement(b,{href:"https://kubernetes.io/"},"k8s")),r.createElement("li",null,"looking for a solution to deeply integrate your frontends with a"," ",r.createElement(b,{href:"https://jamstack.org"},"headless cms"))),r.createElement("sub",null,"If more than two points apply, it will actually help a LOT")),r.createElement("main",null,r.createElement(f,null)))}}}]); \ No newline at end of file diff --git a/assets/js/1df93b7f.6d79ef15.js b/assets/js/1df93b7f.6d79ef15.js new file mode 100644 index 0000000..9c9b356 --- /dev/null +++ b/assets/js/1df93b7f.6d79ef15.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[3237],{8368:(e,t,n)=>{n.r(t),n.d(t,{default:()=>w});var l=n(7294),r=n(6010),o=n(7767),a=n(2263);const c="heroBanner_qdFl";var i=n(7462),s=n(9960);const m="features_xdhU",d="feature_eSJM",u="featureSvg__8YW",h=e=>l.createElement(s.Z,{className:"button button--secondary",to:e.to},e.children),p=[{title:"General guides",description:l.createElement(l.Fragment,null,"no matter if frontend or backend - there is somtehing for everyone in here"),callToAction:"get started",to:"/docs/general/intro"},{title:"Frontend guides",description:l.createElement(l.Fragment,null,"Build frontends with TypeScript, Next.js, styled components, gotsrpc"),callToAction:"go build a frontend",to:"/docs/frontend/intro"},{title:"Backend guides",description:l.createElement(l.Fragment,null,"Write fast and reliable services in Go and run them in the cloud"),callToAction:"build a server",to:"/docs/backend/intro"},{title:"Projects",description:l.createElement(l.Fragment,null,"Foomo projects - libraries, utilities, friendly daemons and more"),callToAction:"explore",to:"/docs/projects/intro"},{title:"CMS",description:l.createElement(l.Fragment,null,"foomo has extensive support for headless CMS Systems"),callToAction:"learn the patterns",to:"/docs/projects/cms/intro"},{title:"Architecture",description:l.createElement(l.Fragment,null,"Show me the big picture - how does foomo integrate into the k8s and Next.js eco system"),callToAction:"explore the diagrams",to:"/docs/projects/architecture"},{title:"Awesome Software",description:l.createElement(l.Fragment,null,"a curated list of software we use in the cloud, on our desktops and our devices"),callToAction:"show me the list",to:"/awesome-software"},{title:"DevOps",description:l.createElement(l.Fragment,null,"our approach to run cloud native applications where they belong"),callToAction:"take me to the cloud",to:"/docs/devops/intro"},{title:"Blog",description:l.createElement(l.Fragment,null,"random information about programming, our ecosystem and misc fun topics"),callToAction:"read the blog",to:"/blog"}];function g(e){let{title:t,image:n,description:r,to:o,callToAction:a}=e;return l.createElement("div",{className:"col col--4 "},l.createElement("div",{className:d},l.createElement("div",{className:"text--center"},n&&l.createElement("img",{className:u,alt:t,src:n})),l.createElement("div",{className:"text--center padding-horiz--md"},l.createElement("h3",null,t),l.createElement("p",null,r)),l.createElement("div",{className:"text--center"},l.createElement(h,{to:o},a))))}function f(){return l.createElement("section",{className:m},l.createElement("div",{className:"container"},l.createElement("div",{className:"row"},p.map(((e,t)=>l.createElement(g,(0,i.Z)({key:t},e)))))))}const E=()=>{const{siteConfig:e}=(0,a.Z)();return l.createElement("header",{className:(0,r.Z)("hero hero--primary",c)},l.createElement("div",{className:"container"},l.createElement("img",{style:{width:"200px",height:"200px",backgroundColor:"black",border:"1px white solid",borderRadius:"1rem"},src:"https://avatars.githubusercontent.com/u/889755"}),l.createElement("h1",{className:"hero__title"},e.title),l.createElement("p",{className:"hero__subtitle"},l.createElement("q",null,e.tagline))))},b=e=>l.createElement("span",{style:{fontWeight:"bold",backgroundColor:"#ffea00",border:"none",padding:"1px 4px",textShadow:"1px 1px 2px #00000081"},title:e.href},l.createElement("a",{href:e.href,style:{color:"white"}},e.children));function w(){const{siteConfig:e}=(0,a.Z)();return l.createElement(o.Z,{title:`Hello from ${e.title}`,description:"Description will go into a meta tag in "},l.createElement(E,null),l.createElement("div",{className:"container",style:{maxWidth:"640px",marginLeft:"auto",marginRight:"auto"}},l.createElement("br",null),l.createElement("p",null,l.createElement("b",null,"foomo")," is an open source project, that has been maintained by the"," ",l.createElement(b,{href:"https://www.bestbytes.com"},"bestbytes")," ","team since 2011. It provides a wide range of utilities, libraries and daemons, that help us to tackle challenging projects."),l.createElement("h2",null,"foomo can help, if you are"),l.createElement("ul",null,l.createElement("li",null,"using"," ",l.createElement(b,{href:"https://www.golang.org"},"Go")," to write services"),l.createElement("li",null,"building frontends with"," ",l.createElement(b,{href:"https://www.typescriptlang.org/"},"TypeScript")," ","and"," ",l.createElement(b,{href:"https://nextjs.org/"},"Next.js")),l.createElement("li",null,"running your software on"," ",l.createElement(b,{href:"https://kubernetes.io/"},"k8s")),l.createElement("li",null,"looking for a solution to deeply integrate your frontends with a"," ",l.createElement(b,{href:"https://jamstack.org"},"headless cms"))),l.createElement("sub",null,"If more than two points apply, it will actually help a LOT")),l.createElement("main",null,l.createElement(f,null)))}}}]); \ No newline at end of file diff --git a/assets/js/1f391b9e.612a1c5c.js b/assets/js/1f391b9e.612a1c5c.js deleted file mode 100644 index 61b166d..0000000 --- a/assets/js/1f391b9e.612a1c5c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[3085],{6416:function(e,a,n){n.r(a),n.d(a,{default:function(){return d}});var t=n(7294),l=n(6010),i=n(8882),c=n(3905),s=n(9014),r=n(1575),m=n(6681),o="mdxPageWrapper_eQvw";var d=function(e){var a=e.content,n=a.frontMatter,d=a.metadata,v=n.title,u=n.description,f=n.wrapperClassName,N=n.hide_table_of_contents,g=d.permalink;return t.createElement(i.Z,{title:v,description:u,permalink:g,wrapperClassName:null!=f?f:m.kM.wrapper.mdxPages,pageClassName:m.kM.page.mdxPage},t.createElement("main",{className:"container container--fluid margin-vert--lg"},t.createElement("div",{className:(0,l.Z)("row",o)},t.createElement("div",{className:(0,l.Z)("col",!N&&"col--8")},t.createElement(c.Zo,{components:s.Z},t.createElement(a,null))),!N&&a.toc&&t.createElement("div",{className:"col col--2"},t.createElement(r.Z,{toc:a.toc,minHeadingLevel:n.toc_min_heading_level,maxHeadingLevel:n.toc_max_heading_level})))))}},1575:function(e,a,n){n.d(a,{Z:function(){return o}});var t=n(3117),l=n(102),i=n(7294),c=n(6010),s=n(5002),r="tableOfContents_vrFS",m=["className"];var o=function(e){var a=e.className,n=(0,l.Z)(e,m);return i.createElement("div",{className:(0,c.Z)(r,"thin-scrollbar",a)},i.createElement(s.Z,(0,t.Z)({},n,{linkClassName:"table-of-contents__link toc-highlight",linkActiveClassName:"table-of-contents__link--active"})))}},5002:function(e,a,n){n.d(a,{Z:function(){return m}});var t=n(3117),l=n(102),i=n(7294),c=n(6681),s=["toc","className","linkClassName","linkActiveClassName","minHeadingLevel","maxHeadingLevel"];function r(e){var a=e.toc,n=e.className,t=e.linkClassName,l=e.isChild;return a.length?i.createElement("ul",{className:l?void 0:n},a.map((function(e){return i.createElement("li",{key:e.id},i.createElement("a",{href:"#"+e.id,className:null!=t?t:void 0,dangerouslySetInnerHTML:{__html:e.value}}),i.createElement(r,{isChild:!0,toc:e.children,className:n,linkClassName:t}))}))):null}function m(e){var a=e.toc,n=e.className,m=void 0===n?"table-of-contents table-of-contents__left-border":n,o=e.linkClassName,d=void 0===o?"table-of-contents__link":o,v=e.linkActiveClassName,u=void 0===v?void 0:v,f=e.minHeadingLevel,N=e.maxHeadingLevel,g=(0,l.Z)(e,s),k=(0,c.LU)(),C=null!=f?f:k.tableOfContents.minHeadingLevel,_=null!=N?N:k.tableOfContents.maxHeadingLevel,p=(0,c.DA)({toc:a,minHeadingLevel:C,maxHeadingLevel:_}),h=(0,i.useMemo)((function(){if(d&&u)return{linkClassName:d,linkActiveClassName:u,minHeadingLevel:C,maxHeadingLevel:_}}),[d,u,C,_]);return(0,c.Si)(h),i.createElement(r,(0,t.Z)({toc:p,className:m,linkClassName:d},g))}}}]); \ No newline at end of file diff --git a/assets/js/1f391b9e.f573fb1e.js b/assets/js/1f391b9e.f573fb1e.js new file mode 100644 index 0000000..b709b67 --- /dev/null +++ b/assets/js/1f391b9e.f573fb1e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[3085],{4247:(e,n,t)=>{t.r(n),t.d(n,{default:()=>d});var l=t(7294),a=t(6010),r=t(833),c=t(5281),i=t(7767),o=t(5203),s=t(9407);const m="mdxPageWrapper_j9I6";function d(e){const{content:n}=e,{metadata:{title:t,description:d,frontMatter:u}}=n,{wrapperClassName:f,hide_table_of_contents:v}=u;return l.createElement(r.FG,{className:(0,a.Z)(f??c.k.wrapper.mdxPages,c.k.page.mdxPage)},l.createElement(r.d,{title:t,description:d}),l.createElement(i.Z,null,l.createElement("main",{className:"container container--fluid margin-vert--lg"},l.createElement("div",{className:(0,a.Z)("row",m)},l.createElement("div",{className:(0,a.Z)("col",!v&&"col--8")},l.createElement("article",null,l.createElement(o.Z,null,l.createElement(n,null)))),!v&&n.toc.length>0&&l.createElement("div",{className:"col col--2"},l.createElement(s.Z,{toc:n.toc,minHeadingLevel:u.toc_min_heading_level,maxHeadingLevel:u.toc_max_heading_level}))))))}},9407:(e,n,t)=>{t.d(n,{Z:()=>o});var l=t(7462),a=t(7294),r=t(6010),c=t(3743);const i="tableOfContents_bqdL";function o(e){let{className:n,...t}=e;return a.createElement("div",{className:(0,r.Z)(i,"thin-scrollbar",n)},a.createElement(c.Z,(0,l.Z)({},t,{linkClassName:"table-of-contents__link toc-highlight",linkActiveClassName:"table-of-contents__link--active"})))}},3743:(e,n,t)=>{t.d(n,{Z:()=>v});var l=t(7462),a=t(7294),r=t(6668);function c(e){const n=e.map((e=>({...e,parentIndex:-1,children:[]}))),t=Array(7).fill(-1);n.forEach(((e,n)=>{const l=t.slice(2,e.level);e.parentIndex=Math.max(...l),t[e.level]=n}));const l=[];return n.forEach((e=>{const{parentIndex:t,...a}=e;t>=0?n[t].children.push(a):l.push(a)})),l}function i(e){let{toc:n,minHeadingLevel:t,maxHeadingLevel:l}=e;return n.flatMap((e=>{const n=i({toc:e.children,minHeadingLevel:t,maxHeadingLevel:l});return function(e){return e.level>=t&&e.level<=l}(e)?[{...e,children:n}]:n}))}function o(e){const n=e.getBoundingClientRect();return n.top===n.bottom?o(e.parentNode):n}function s(e,n){let{anchorTopOffset:t}=n;const l=e.find((e=>o(e).top>=t));if(l){return function(e){return e.top>0&&e.bottom{e.current=n?0:document.querySelector(".navbar").clientHeight}),[n]),e}function d(e){const n=(0,a.useRef)(void 0),t=m();(0,a.useEffect)((()=>{if(!e)return()=>{};const{linkClassName:l,linkActiveClassName:a,minHeadingLevel:r,maxHeadingLevel:c}=e;function i(){const e=function(e){return Array.from(document.getElementsByClassName(e))}(l),i=function(e){let{minHeadingLevel:n,maxHeadingLevel:t}=e;const l=[];for(let a=n;a<=t;a+=1)l.push(`h${a}.anchor`);return Array.from(document.querySelectorAll(l.join()))}({minHeadingLevel:r,maxHeadingLevel:c}),o=s(i,{anchorTopOffset:t.current}),m=e.find((e=>o&&o.id===function(e){return decodeURIComponent(e.href.substring(e.href.indexOf("#")+1))}(e)));e.forEach((e=>{!function(e,t){t?(n.current&&n.current!==e&&n.current.classList.remove(a),e.classList.add(a),n.current=e):e.classList.remove(a)}(e,e===m)}))}return document.addEventListener("scroll",i),document.addEventListener("resize",i),i(),()=>{document.removeEventListener("scroll",i),document.removeEventListener("resize",i)}}),[e,t])}function u(e){let{toc:n,className:t,linkClassName:l,isChild:r}=e;return n.length?a.createElement("ul",{className:r?void 0:t},n.map((e=>a.createElement("li",{key:e.id},a.createElement("a",{href:`#${e.id}`,className:l??void 0,dangerouslySetInnerHTML:{__html:e.value}}),a.createElement(u,{isChild:!0,toc:e.children,className:t,linkClassName:l}))))):null}const f=a.memo(u);function v(e){let{toc:n,className:t="table-of-contents table-of-contents__left-border",linkClassName:o="table-of-contents__link",linkActiveClassName:s,minHeadingLevel:m,maxHeadingLevel:u,...v}=e;const g=(0,r.L)(),h=m??g.tableOfContents.minHeadingLevel,L=u??g.tableOfContents.maxHeadingLevel,p=function(e){let{toc:n,minHeadingLevel:t,maxHeadingLevel:l}=e;return(0,a.useMemo)((()=>i({toc:c(n),minHeadingLevel:t,maxHeadingLevel:l})),[n,t,l])}({toc:n,minHeadingLevel:h,maxHeadingLevel:L});return d((0,a.useMemo)((()=>{if(o&&s)return{linkClassName:o,linkActiveClassName:s,minHeadingLevel:h,maxHeadingLevel:L}}),[o,s,h,L])),a.createElement(f,(0,l.Z)({toc:p,className:t,linkClassName:o},v))}}}]); \ No newline at end of file diff --git a/assets/js/2529.d8450ead.js b/assets/js/2529.d8450ead.js new file mode 100644 index 0000000..da80f85 --- /dev/null +++ b/assets/js/2529.d8450ead.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[2529],{9058:(e,t,a)=>{a.d(t,{Z:()=>N});var l=a(7294),n=a(6010),r=a(7767),s=a(7524),o=a(9960),i=a(5999);const c="sidebar_re4s",m="sidebarItemTitle_pO2u",u="sidebarItemList_Yudw",d="sidebarItem__DBe",g="sidebarItemLink_mo7H",p="sidebarItemLinkActive_I1ZP";function h(e){let{sidebar:t}=e;return l.createElement("aside",{className:"col col--3"},l.createElement("nav",{className:(0,n.Z)(c,"thin-scrollbar"),"aria-label":(0,i.I)({id:"theme.blog.sidebar.navAriaLabel",message:"Blog recent posts navigation",description:"The ARIA label for recent posts in the blog sidebar"})},l.createElement("div",{className:(0,n.Z)(m,"margin-bottom--md")},t.title),l.createElement("ul",{className:(0,n.Z)(u,"clean-list")},t.items.map((e=>l.createElement("li",{key:e.permalink,className:d},l.createElement(o.Z,{isNavLink:!0,to:e.permalink,className:g,activeClassName:p},e.title)))))))}var E=a(3102);function f(e){let{sidebar:t}=e;return l.createElement("ul",{className:"menu__list"},t.items.map((e=>l.createElement("li",{key:e.permalink,className:"menu__list-item"},l.createElement(o.Z,{isNavLink:!0,to:e.permalink,className:"menu__link",activeClassName:"menu__link--active"},e.title)))))}function b(e){return l.createElement(E.Zo,{component:f,props:e})}function v(e){let{sidebar:t}=e;const a=(0,s.i)();return null!=t&&t.items.length?"mobile"===a?l.createElement(b,{sidebar:t}):l.createElement(h,{sidebar:t}):null}function N(e){const{sidebar:t,toc:a,children:s,...o}=e,i=t&&t.items.length>0;return l.createElement(r.Z,o,l.createElement("div",{className:"container margin-vert--lg"},l.createElement("div",{className:"row"},l.createElement(v,{sidebar:t}),l.createElement("main",{className:(0,n.Z)("col",{"col--7":i,"col--9 col--offset-1":!i}),itemScope:!0,itemType:"http://schema.org/Blog"},s),a&&l.createElement("div",{className:"col col--2"},a))))}},390:(e,t,a)=>{a.d(t,{Z:()=>R});var l=a(7294),n=a(6010),r=a(9460),s=a(4996);function o(e){let{children:t,className:a}=e;const{frontMatter:n,assets:o}=(0,r.C)(),{withBaseUrl:i}=(0,s.C)(),c=o.image??n.image;return l.createElement("article",{className:a,itemProp:"blogPost",itemScope:!0,itemType:"http://schema.org/BlogPosting"},c&&l.createElement("meta",{itemProp:"image",content:i(c,{absolute:!0})}),t)}var i=a(9960);const c="title_f1Hy";function m(e){let{className:t}=e;const{metadata:a,isBlogPostPage:s}=(0,r.C)(),{permalink:o,title:m}=a,u=s?"h1":"h2";return l.createElement(u,{className:(0,n.Z)(c,t),itemProp:"headline"},s?m:l.createElement(i.Z,{itemProp:"url",to:o},m))}var u=a(5999),d=a(8824);const g="container_mt6G";function p(e){let{readingTime:t}=e;const a=function(){const{selectMessage:e}=(0,d.c)();return t=>{const a=Math.ceil(t);return e(a,(0,u.I)({id:"theme.blog.post.readingTime.plurals",description:'Pluralized label for "{readingTime} min read". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',message:"One min read|{readingTime} min read"},{readingTime:a}))}}();return l.createElement(l.Fragment,null,a(t))}function h(e){let{date:t,formattedDate:a}=e;return l.createElement("time",{dateTime:t,itemProp:"datePublished"},a)}function E(){return l.createElement(l.Fragment,null," \xb7 ")}function f(e){let{className:t}=e;const{metadata:a}=(0,r.C)(),{date:s,formattedDate:o,readingTime:i}=a;return l.createElement("div",{className:(0,n.Z)(g,"margin-vert--md",t)},l.createElement(h,{date:s,formattedDate:o}),void 0!==i&&l.createElement(l.Fragment,null,l.createElement(E,null),l.createElement(p,{readingTime:i})))}function b(e){return e.href?l.createElement(i.Z,e):l.createElement(l.Fragment,null,e.children)}function v(e){let{author:t,className:a}=e;const{name:r,title:s,url:o,imageURL:i,email:c}=t,m=o||c&&`mailto:${c}`||void 0;return l.createElement("div",{className:(0,n.Z)("avatar margin-bottom--sm",a)},i&&l.createElement(b,{href:m,className:"avatar__photo-link"},l.createElement("img",{className:"avatar__photo",src:i,alt:r})),r&&l.createElement("div",{className:"avatar__intro",itemProp:"author",itemScope:!0,itemType:"https://schema.org/Person"},l.createElement("div",{className:"avatar__name"},l.createElement(b,{href:m,itemProp:"url"},l.createElement("span",{itemProp:"name"},r))),s&&l.createElement("small",{className:"avatar__subtitle",itemProp:"description"},s)))}const N="authorCol_Hf19",_="imageOnlyAuthorRow_pa_O",Z="imageOnlyAuthorCol_G86a";function P(e){let{className:t}=e;const{metadata:{authors:a},assets:s}=(0,r.C)();if(0===a.length)return null;const o=a.every((e=>{let{name:t}=e;return!t}));return l.createElement("div",{className:(0,n.Z)("margin-top--md margin-bottom--sm",o?_:"row",t)},a.map(((e,t)=>l.createElement("div",{className:(0,n.Z)(!o&&"col col--6",o?Z:N),key:t},l.createElement(v,{author:{...e,imageURL:s.authorsImageUrls[t]??e.imageURL}})))))}function k(){return l.createElement("header",null,l.createElement(m,null),l.createElement(f,null),l.createElement(P,null))}var T=a(8780),w=a(5203);function C(e){let{children:t,className:a}=e;const{isBlogPostPage:s}=(0,r.C)();return l.createElement("div",{id:s?T.blogPostContainerID:void 0,className:(0,n.Z)("markdown",a),itemProp:"articleBody"},l.createElement(w.Z,null,t))}var y=a(4881),B=a(1526),F=a(7462);function I(){return l.createElement("b",null,l.createElement(u.Z,{id:"theme.blog.post.readMore",description:"The label used in blog post item excerpts to link to full blog posts"},"Read More"))}function x(e){const{blogPostTitle:t,...a}=e;return l.createElement(i.Z,(0,F.Z)({"aria-label":(0,u.I)({message:"Read more about {title}",id:"theme.blog.post.readMoreLabel",description:"The ARIA label for the link to full blog posts from excerpts"},{title:t})},a),l.createElement(I,null))}const L="blogPostFooterDetailsFull_mRVl";function M(){const{metadata:e,isBlogPostPage:t}=(0,r.C)(),{tags:a,title:s,editUrl:o,hasTruncateMarker:i}=e,c=!t&&i,m=a.length>0;return m||c||o?l.createElement("footer",{className:(0,n.Z)("row docusaurus-mt-lg",t&&L)},m&&l.createElement("div",{className:(0,n.Z)("col",{"col--9":c})},l.createElement(B.Z,{tags:a})),t&&o&&l.createElement("div",{className:"col margin-top--sm"},l.createElement(y.Z,{editUrl:o})),c&&l.createElement("div",{className:(0,n.Z)("col text--right",{"col--3":m})},l.createElement(x,{blogPostTitle:s,to:e.permalink}))):null}function R(e){let{children:t,className:a}=e;const s=function(){const{isBlogPostPage:e}=(0,r.C)();return e?void 0:"margin-bottom--xl"}();return l.createElement(o,{className:(0,n.Z)(s,a)},l.createElement(k,null),l.createElement(C,null,t),l.createElement(M,null))}},4881:(e,t,a)=>{a.d(t,{Z:()=>m});var l=a(7294),n=a(5999),r=a(5281),s=a(7462),o=a(6010);const i="iconEdit_Z9Sw";function c(e){let{className:t,...a}=e;return l.createElement("svg",(0,s.Z)({fill:"currentColor",height:"20",width:"20",viewBox:"0 0 40 40",className:(0,o.Z)(i,t),"aria-hidden":"true"},a),l.createElement("g",null,l.createElement("path",{d:"m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"})))}function m(e){let{editUrl:t}=e;return l.createElement("a",{href:t,target:"_blank",rel:"noreferrer noopener",className:r.k.common.editThisPage},l.createElement(c,null),l.createElement(n.Z,{id:"theme.common.editThisPage",description:"The link label to edit the current page"},"Edit this page"))}},2244:(e,t,a)=>{a.d(t,{Z:()=>s});var l=a(7294),n=a(6010),r=a(9960);function s(e){const{permalink:t,title:a,subLabel:s,isNext:o}=e;return l.createElement(r.Z,{className:(0,n.Z)("pagination-nav__link",o?"pagination-nav__link--next":"pagination-nav__link--prev"),to:t},s&&l.createElement("div",{className:"pagination-nav__sublabel"},s),l.createElement("div",{className:"pagination-nav__label"},a))}},3008:(e,t,a)=>{a.d(t,{Z:()=>c});var l=a(7294),n=a(6010),r=a(9960);const s="tag_zVej",o="tagRegular_sFm0",i="tagWithCount_h2kH";function c(e){let{permalink:t,label:a,count:c}=e;return l.createElement(r.Z,{href:t,className:(0,n.Z)(s,c?i:o)},a,c&&l.createElement("span",null,c))}},1526:(e,t,a)=>{a.d(t,{Z:()=>c});var l=a(7294),n=a(6010),r=a(5999),s=a(3008);const o="tags_jXut",i="tag_QGVx";function c(e){let{tags:t}=e;return l.createElement(l.Fragment,null,l.createElement("b",null,l.createElement(r.Z,{id:"theme.tags.tagsListLabel",description:"The label alongside a tag list"},"Tags:")),l.createElement("ul",{className:(0,n.Z)(o,"padding--none","margin-left--sm")},t.map((e=>{let{label:t,permalink:a}=e;return l.createElement("li",{key:a,className:i},l.createElement(s.Z,{label:t,permalink:a}))}))))}},9460:(e,t,a)=>{a.d(t,{C:()=>o,n:()=>s});var l=a(7294),n=a(902);const r=l.createContext(null);function s(e){let{children:t,content:a,isBlogPostPage:n=!1}=e;const s=function(e){let{content:t,isBlogPostPage:a}=e;return(0,l.useMemo)((()=>({metadata:t.metadata,frontMatter:t.frontMatter,assets:t.assets,toc:t.toc,isBlogPostPage:a})),[t,a])}({content:a,isBlogPostPage:n});return l.createElement(r.Provider,{value:s},t)}function o(){const e=(0,l.useContext)(r);if(null===e)throw new n.i6("BlogPostProvider");return e}},8824:(e,t,a)=>{a.d(t,{c:()=>c});var l=a(7294),n=a(2263);const r=["zero","one","two","few","many","other"];function s(e){return r.filter((t=>e.includes(t)))}const o={locale:"en",pluralForms:s(["one","other"]),select:e=>1===e?"one":"other"};function i(){const{i18n:{currentLocale:e}}=(0,n.Z)();return(0,l.useMemo)((()=>{try{return function(e){const t=new Intl.PluralRules(e);return{locale:e,pluralForms:s(t.resolvedOptions().pluralCategories),select:e=>t.select(e)}}(e)}catch(t){return console.error(`Failed to use Intl.PluralRules for locale "${e}".\nDocusaurus will fallback to the default (English) implementation.\nError: ${t.message}\n`),o}}),[e])}function c(){const e=i();return{selectMessage:(t,a)=>function(e,t,a){const l=e.split("|");if(1===l.length)return l[0];l.length>a.pluralForms.length&&console.error(`For locale=${a.locale}, a maximum of ${a.pluralForms.length} plural forms are expected (${a.pluralForms.join(",")}), but the message contains ${l.length}: ${e}`);const n=a.select(t),r=a.pluralForms.indexOf(n);return l[Math.min(r,l.length-1)]}(a,t,e)}}}}]); \ No newline at end of file diff --git a/assets/js/26ea2a44.19014068.js b/assets/js/26ea2a44.19014068.js new file mode 100644 index 0000000..f685ca2 --- /dev/null +++ b/assets/js/26ea2a44.19014068.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[1435],{3905:(A,e,t)=>{t.d(e,{Zo:()=>g,kt:()=>c});var l=t(7294);function n(A,e,t){return e in A?Object.defineProperty(A,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):A[e]=t,A}function a(A,e){var t=Object.keys(A);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(A);e&&(l=l.filter((function(e){return Object.getOwnPropertyDescriptor(A,e).enumerable}))),t.push.apply(t,l)}return t}function o(A){for(var e=1;e=0||(n[t]=A[t]);return n}(A,e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(A);for(l=0;l=0||Object.prototype.propertyIsEnumerable.call(A,t)&&(n[t]=A[t])}return n}var r=l.createContext({}),s=function(A){var e=l.useContext(r),t=e;return A&&(t="function"==typeof A?A(e):o(o({},e),A)),t},g=function(A){var e=s(A.components);return l.createElement(r.Provider,{value:e},A.children)},u={inlineCode:"code",wrapper:function(A){var e=A.children;return l.createElement(l.Fragment,{},e)}},p=l.forwardRef((function(A,e){var t=A.components,n=A.mdxType,a=A.originalType,r=A.parentName,g=i(A,["components","mdxType","originalType","parentName"]),p=s(t),c=n,d=p["".concat(r,".").concat(c)]||p[c]||u[c]||a;return t?l.createElement(d,o(o({ref:e},g),{},{components:t})):l.createElement(d,o({ref:e},g))}));function c(A,e){var t=arguments,n=e&&e.mdxType;if("string"==typeof A||n){var a=t.length,o=new Array(a);o[0]=p;var i={};for(var r in e)hasOwnProperty.call(e,r)&&(i[r]=e[r]);i.originalType=A,i.mdxType="string"==typeof A?A:n,o[1]=i;for(var s=2;s{t.r(e),t.d(e,{assets:()=>r,contentTitle:()=>o,default:()=>u,frontMatter:()=>a,metadata:()=>i,toc:()=>s});var l=t(7462),n=(t(7294),t(3905));const a={slug:"why-bundle-size-is-important",authors:["nicola"],tags:["javascript","bundle","bundle size"]},o="Why bundle size is important?",i={permalink:"/blog/why-bundle-size-is-important",editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/blog/2022-03-17-why-bundle-size-is-important/index.mdx",source:"@site/blog/2022-03-17-why-bundle-size-is-important/index.mdx",title:"Why bundle size is important?",description:"Intro",date:"2022-03-17T00:00:00.000Z",formattedDate:"March 17, 2022",tags:[{label:"javascript",permalink:"/blog/tags/javascript"},{label:"bundle",permalink:"/blog/tags/bundle"},{label:"bundle size",permalink:"/blog/tags/bundle-size"}],hasTruncateMarker:!1,authors:[{name:"Nicola Turcato",title:"Memelord brother",url:"https://github.com/nicolaturcato",imageURL:"https://github.com/nicolaturcato.png",key:"nicola"}],frontMatter:{slug:"why-bundle-size-is-important",authors:["nicola"],tags:["javascript","bundle","bundle size"]},nextItem:{title:"Prometheus Is Out Of Memory. Again.",permalink:"/blog/prometheus-cardinality-issues"}},r={authorsImageUrls:[void 0]},s=[{value:"Intro",id:"intro",level:2},{value:"Nobody likes waiting\u2026",id:"nobody-likes-waiting",level:2},{value:"What is a "bundle"?",id:"what-is-a-bundle",level:2},{value:"Performance implications",id:"performance-implications",level:2},{value:"What is the recommended bundle size?",id:"what-is-the-recommended-bundle-size",level:2},{value:"What do we do then?",id:"what-do-we-do-then",level:2},{value:"How to start decreasing the bundle size?",id:"how-to-start-decreasing-the-bundle-size",level:2},{value:"Breaking up the bundle...",id:"breaking-up-the-bundle",level:2},{value:"Which strategies can we adopt?",id:"which-strategies-can-we-adopt",level:2},{value:"Useful tools to help you reducing bundle size",id:"useful-tools-to-help-you-reducing-bundle-size",level:2},{value:"Conclusion",id:"conclusion",level:2}],g={toc:s};function u(A){let{components:e,...a}=A;return(0,n.kt)("wrapper",(0,l.Z)({},g,a,{components:e,mdxType:"MDXLayout"}),(0,n.kt)("h2",{id:"intro"},"Intro"),(0,n.kt)("p",null,"JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website."),(0,n.kt)("p",null,"Frontend performance optimization is critical because\xa0it accounts for around 80-90% of user response time (10-20% backend).\nSo when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets."),(0,n.kt)("h2",{id:"nobody-likes-waiting"},"Nobody likes waiting\u2026"),(0,n.kt)("p",null,"A\xa0study found\xa0that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site."),(0,n.kt)("p",null,"Sending large JavaScript payloads impacts the speed of your site significantly."),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"Mazzarri",src:t(7017).Z,width:"410",height:"276"})),(0,n.kt)("h2",{id:"what-is-a-bundle"},'What is a "bundle"?'),(0,n.kt)("p",null,"Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application."),(0,n.kt)("p",null,"JS bundling is\xa0an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests."),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"Bundle Everywhere",src:t(6865).Z,width:"448",height:"245"})),(0,n.kt)("h2",{id:"performance-implications"},"Performance implications"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Time to transmit over the network"),": considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"JS parse and compile time"),": more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"JS execution time"),": optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Memory consumption"),": everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!")),(0,n.kt)("h2",{id:"what-is-the-recommended-bundle-size"},"What is the recommended bundle size?"),(0,n.kt)("p",null,"AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly."),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"Mr Chao",src:t(1262).Z,width:"406",height:"250"})),(0,n.kt)("h2",{id:"what-do-we-do-then"},"What do we do then?"),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"Meh",src:t(4812).Z,width:"539",height:"399"})),(0,n.kt)("h2",{id:"how-to-start-decreasing-the-bundle-size"},"How to start decreasing the bundle size?"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Measure"),": first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI)\xa0is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Analyze"),": Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.")),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"Stonks",src:t(3400).Z,width:"309",height:"229"})),(0,n.kt)("h2",{id:"breaking-up-the-bundle"},"Breaking up the bundle..."),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Monitor network requests"),": These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Reduce the total dom nodes"),": the less the page needs to render, the less time it takes."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Moving work off the main thread"),": By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Caching"),": Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast")),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"Breaking Bad",src:t(1278).Z,width:"299",height:"230"})),(0,n.kt)("h2",{id:"which-strategies-can-we-adopt"},"Which strategies can we adopt?"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Minification and Dead Code Elimination"),": These processes are often summed up as\xa0minifying\xa0or\xa0uglifying."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Tree shaking"),": Tree shaking is dead code elimination on a project or library. Always try to use deps which support \u201ctree shaking\u201d, Bundlephobia could be your friend in this case."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Code Splitting and Lazy Loading"),": Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Replace/rewrite large dependencies"),": Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example)."),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Feature module import"),": Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).")),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"Strategy",src:t(2235).Z,width:"307",height:"204"})),(0,n.kt)("h2",{id:"useful-tools-to-help-you-reducing-bundle-size"},"Useful tools to help you reducing bundle size"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Lighthouse"),": automated tool for improving the performance, quality, and correctness of your web apps"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Bundlephobia"),": Bundlephobia\xa0helps you find the performance impact of npm packages"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"Webpack Bundle Analyzer"),": analyzes your bundle"),(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("strong",{parentName:"li"},"VS Code"),": Import Cost plugin -> Display import/require package size in the editor")),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"Tools",src:t(4941).Z,width:"345",height:"255"})),(0,n.kt)("h2",{id:"conclusion"},"Conclusion"),(0,n.kt)("p",null,"Performance cannot be stripped down to a single metric such as bundle size. It would be great!\nUnfortunately there is no single place to measure all of them.I think metrics like the\xa0Core Web Vitals and a general look at bundle size should be considered as a starting point.\nYou will cry... A lot... But don\u2019t give up!"),(0,n.kt)("p",null,(0,n.kt)("img",{alt:"The End",src:t(6899).Z,width:"358",height:"260"})))}u.isMDXComponent=!0},1278:(A,e,t)=>{t.d(e,{Z:()=>l});const l="data:image/webp;base64,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"},6865:(A,e,t)=>{t.d(e,{Z:()=>l});const l=t.p+"assets/images/bundle_everywhere-12122e37ab5ca6bcdd61061c4dc32fd4.webp"},7017:(A,e,t)=>{t.d(e,{Z:()=>l});const l="data:image/webp;base64,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FzF4MYb5dPUHhs5cr4W48fbG0YBO8KzHr+LA17KHdZxYMTAAUpcIp1VnAxgo31955bEsLblDFuBy5dlIEDsrWBpK464x+XXAKVkwXRr/6wHYa71OvfyxCA1GDVeOMppgCM+SdSSV+Z4ZOovufzwbrFiK3sBdeld88AOcgwYxb7qpz7mhGS+zB64o5jXBTZSyA0MDXdYQvBoDLJ20qMaLfoJFEqWGz2rC81rxT/bY7gBgPtFqMlpyIMYi4u3Yf1172FNN4/W/xvmZprZO3IbwFq9PQMoJbQXQrwxVYYZwvWGXERPk2Uhlz9gRWmzNaVqkp7mubKLzLAUJ6lcuebTvMqkcxJJymwmZtTUr5Gxvd/HWyOx2AAfyO55mL93wAKh7ARoMajGsPnAMsEfBuYIWIdlP2J4tFsRWmhppa6AQ0cfFbM4a8+RPS2EsWikEK0LhB4GwUWRPGqjgRduFXpKdIIclPO1V8FWUIErCbCAPvTG621JcUfAF9zBHAyEDUplreZZ7sJWMytQpAAkQo1oYHokzkMAkASVnsb09gWQHFj9DRXBQN7QHdbarq61G9+bdIH7RjBlmBo2PkcAwx6UQB2Gs+3fjaO2CxPFOSFo3qLw54OIXVcwaQOoFyZksQpx38MAMv8LX68Pl6brrz7OlB1oNcsJNDiE7+FeHZ/8/I0oZAAAARVhJRlYAAABNTQAqAAAACAABh2kABAAAAAEAAAAaAAAAAAADkoYABwAAABIAAABEoAIABAAAAAEAAAGaoAMABAAAAAEAAAEUAAAAAEFTQ0lJAAAAU2NyZWVuc2hvdFhNUCDAAQAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4yNzY8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NDEwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6VXNlckNvbW1lbnQ+U2NyZWVuc2hvdDwvZXhpZjpVc2VyQ29tbWVudD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cg=="},4812:(A,e,t)=>{t.d(e,{Z:()=>l});const l=t.p+"assets/images/meh-3b42aead7d2246467fd0101518403734.webp"},1262:(A,e,t)=>{t.d(e,{Z:()=>l});const l="data:image/webp;base64,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"},3400:(A,e,t)=>{t.d(e,{Z:()=>l});const l="data:image/webp;base64,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"},2235:(A,e,t)=>{t.d(e,{Z:()=>l});const l="data:image/webp;base64,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"},6899:(A,e,t)=>{t.d(e,{Z:()=>l});const l="data:image/webp;base64,UklGRhIiAABXRUJQVlA4WAoAAAAsAAAAZQEAAwEASUNDUBQCAAAAAAIUYXBwbAQAAABtbnRyUkdCIFhZWiAH5gADABEACAAyADdhY3NwQVBQTAAAAABBUFBMAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGycI4tw2obgutNn6fT9ab4VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApkZXNjAAAA/AAAAGVjcHJ0AAABZAAAACN3dHB0AAABiAAAABRyWFlaAAABnAAAABRnWFlaAAABsAAAABRiWFlaAAABxAAAABRyVFJDAAAB2AAAABBjaGFkAAAB6AAAACxiVFJDAAAB2AAAABBnVFJDAAAB2AAAABBkZXNjAAAAAAAAAAtST0cgUEcyNDhRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIEluYy4sIDIwMjIAAFhZWiAAAAAAAADzUgABAAAAARa+WFlaIAAAAAAAAG+kAAA49gAAA5FYWVogAAAAAAAAYpQAALeGAAAY2lhZWiAAAAAAAAAkngAAD4QAALbCcGFyYQAAAAAAAAAAAAH2BHNmMzIAAAAAAAEMPwAABd3///MoAAAHkQAA/ZH///uj///9owAAA9sAAMB5VlA4ILIdAAAQpwCdASpmAQQBPpE6mEeloyKhK9Wd0LASCU3ECXcmYiUAQ0Ku96r9jfTW5H70PiUgJ7j0l5u/+r7AfF96jnmqpqvH9XvuxvVfYX/htzH/heEf7fm9goeVz4n6RvJq/DdESoAK+SRKjKksi/yRCc0V+eA/vVYRk6XuqAYGExwXDTFcqJv/MgIUmP8LmBxSWOCb8doXkFhYA9b5e3+gCb/2vra0ZIY9ceXGm0hFQ0fkVUA/OmHD3TinU7fwH65HT8sfrYfFQK/8/dAmmikKkWFS9ml67dsfvwcKciJHLzsrFYWc2AXki39z8vt6XU5pg2KXhOkeuU6U8OTkFWlY/MgLuPFfSOKU/TcD68/sNRILrWuqizlOQlhdci5546ZlL0148ngdk9ApZ/HEdH9v0Z4XqsGZuUCmlU+cgFVGXDV2gG7Eg7eOVMJqx5C89nQG9jF2jn8d6++6Yzdfe2xzcKBmVfcAQ+07G4Puy7wWvZkYHypFZgFPuEfTu+Wd5HpBme9AHbr5PuRqTsjmfklP828GWCgx2+162ynH8donGtdbeUAy+I61URZSlX9YdqRVVNibQSdExb6p+SS9JEHgs+1eHXs0d1x2cFJtqk6A0a5sXSzJCb0e7CXnkvxwE7MjS/prTYCKtCJgVgiHK1XM1Rly/WKpKjyB1moWIncBo3PgvMj5loTqA+rDzeRYx2yo5ey4dvDWf8vUIaaJDcjHxKNDwBqE73PExe8Bz6O4j1EKif6dUbi2Dze/JlJ8p24NnHQDS/rS656w/6w/fX7ecFBPBHiCGjC9AKbeCOP+ZieFTvO5qUic4iqHAyFwNf72PXxxB4sJpPye2F2QjMmqk+zdWhi6tJl36Vr0+1ysOs1tEahvbkFhj8FRX2WF6QTqL+jjfrVSmYFphaDGnhwSucwjtkHO2jfnOy9bAipVGBnT+ity6Y0S8nJglRmwgewPZYvvQMK52HEsgCacl+hteS4dVmJZNZz380ARHfBC8v/XouDpiPchdfgF/KGGm54XXAMbGxf0GvvSpoJimr4HYb8s09pmn9niUw3/iQTBf/atELZnkTNuIPul4Lq6Ag3tJpEprt75mgMkgC3Cn4mnQM05hSIAIiY/Vgme8r6JfBG4Dt+/QyA6lkdbJWHINiY0I2woCiNNPSIfi7Uz1UfOFlXm8Bd7+ZtFrrDo7y/cJoS5cSJbHOQCNHdptbP8/8UXRbonxm1QiqpZc+GeceESl2trZhdYDh25XOXMpQH1PEX88LQFI5uDinbcyoYa6QfhRKI4VKCB78KNLypA4NPyjGeDStTHdITU0jSI6fE9e19zIpUaAX/CnqncOsk6lEug1BC7UGcOYelhyzHAY7U1B68L1zKTaTHCL+/t8RQiJ4+X4hngL5nhmBnmUksKz1oXlzPyPRG8WNRzxz/rFp4o0Q8Tc/t36lq0WZaSs339/wj0SEU05GGmMk9UW3Dz2PgdC62GEmUXpPObjBSOmYRhWq+ALfKNSLzDNu4UM7s3qerSvJAw62aZvYhVAyE6zNArWZO90keO24rm6thp82U9BbFcaZlzlyGGJYdjcivXaeAmMULngB5D/sBgxMTnM67x4On7Aez1kfu7QqVwjbr0VnnbfldYE5waJ7r+DbK6d5yAz37gXz3LtiwdA2BgwT6zlBk5aGRplQrxhDPX0m84lhDFWkxo06W7/Foc2U9Dn6/A+Z+a8LfRkwGgUpWwoNgthN0hG0ETdrzR+v+I3U7jIMI0ZBTkQk6IZD2MFPKboKuos8/k/SEbSIfVsh7GECDCjNcwAKzgHlaEET322Qthf+D+PXBz3lbsq1gjMxPm3+M8OALqfeniSrzTNk76+TWkyZH+pz8jMzP8JJ9mdEc/pfTgMdadI+BnJaZ0Wcm3NvcYk0X1OGAqkytuEp2YNuAc5p5r4b5Sm73vmeQDWUIryR2F6FSsmxVKMK2FE0EbOY3fndvaAI92HyytVSf85p7MWkssIu1raKNlvbuOrYmDof/w/LofWOjh/ligLN30E+IyucGI8m/3YSoSE6rV9PRhv4WdoC2V/6qf0/qZ40lm80A7llP0LSwsjVr4OXnUhqaIVaElgVkvLkFB1U3gNvZXFnq8WLbZyr5oHv+H2962ITRsaWuUkr7+z8BukPBtUbvITNGuLXiYc17taO3T5E3nZs9SXBzyy7FT3Raaqp9+/4n+wXm5jxi2oFrI2rTXJ64XIuXy19Cy2gApoBUkGY8Gg+cDpnO2Z0ygmkM/4kIvHud9O+0EABdXfOZaU35+H5a0jyfXd4UevXus3tMnT/3CHDbg1iRcehkqK/q/ndbAdlEUOeIMZg+9Aofz3LVkhV/jgv91LDXJ//KSdvi3KbdRqsO24VzPnTT/DHy/DC/Cnq6uzO4XRp1SLyz0KTYRdoOR4XlkxlLXC9NAddBLWAVgJ+hrXI/edoYcwoq/PayPy7z94A103vwV43UVeBIjYs7uX4ObevXR/Q5mun4atkjPVW2wNQK8LQ5q+xiysMFObrobSL/+yUG1fj9Fca4+wVte9bnc3tc0qVdNHHbgMweVVK62Txu7DsoZttFmwwPaPCNgdarOhWH9bn3ls5XJwE/u43cc7wSIO/4xwFMpFOehYzGAdr6Gn3Z89Yu1hQGy2D/l2fpprBz3ENmhXl+etamx7zNfp6ngGBlKdiGkprYZQk7RYLppiMTybIlOMSOnS2Zzax+TzDJH8ml1+9gLTjNRTwgNW1oAGIVeOd7WLtUrV/J4LvGnm3i7UjqzA2aYAjETYuvxDV0tRTCn6cHEXenI2gehTgL7lO05AIRyLKNNHO0HnVcfDEmZlDhfXYhVfMQjCK09/OJ4NZI9bpJCG42ZxPmbMywhtmzsF9L3P1zRQwwgxs9Q+0FQDMCz0hF+tZhKQ9iGiKLiImCSWd18RwzsCh5N7Q/aR+e17moK5qRkJW6A3pwgnWD1/d0dBPXepM+PMfSGah0kh9rbtxgHP1iQPQNRrayo6WnfX3aBgJy02EUaDPZzZSqb0naWuGoqv8dnbx1EQjs2ErE40SAKMSgVeKpngYfbvQsccJumkiyuSKEiAd4Zq6fDC9GKKw4O7/NKonQy1gk/yGXxtsJiokiwL46HFvr7DnBZBMv2I9Op6f3BMP6R5Ys3ewzkTqoQRgj4BHB1RCMdV28MEyMSdJNq8y4SkPg/47TSw+Pn5137xukYSV8iZQQ1UThnsyMpkORg/r875F6VUwakob43sTdMRFwbBAQYBUJazlqWrq2ht6/PNF0KeHFeje1eUCfzkpxP/vpD32G9/APX6GecfUjNbMjpJzbogCRe1W+x32Ly///xLrQcguT58JFZETEoA0kz14N4NHBmM2XWoky1w6c6iLWLfNjDOi7SefMoGStthC8WcZ6eg1hJAVBrqk3xk5P+5jkLouaMbFwIsF1rOOnmYnHZlwyoIieQoLyhdCqsDNB3i+FOSmjDNwvH5EfhEJYLnRE4Z1key1+xJcXsDSjMQmQjAAokM55LI6SawZT4CnkxQAbluhb2oseU+Hwlxre9SOw2FtSDXqQJN6Q/27W3MAYXMEugW82AA+bVSfBkPSUV+XM/cb75/EsAkFDAxxAgFObx30Nes8lqwjzacEIqyh2gZkS7UKpUtmXZboaqwZpkarhX3bO+5E3JY3AlmskLRhP1igHnrW674PQ8g+xVeR9PrF1guLxfQz9U2kNUgVfKFWMAy3Gjgltx/jziqIPbCJtkzXGKzB1mZ2NN3gTrJ3GMy3qkFc8Dt+p00q5GjPh4X6vvtUAy7W+tHt2KYyjkUBonU+zClG55AJplREZ7sPi15wA2kWo+Fo/OnEfN6dypwJK6Ol+wwTFs9a8h8FQWWSX1rRv+sCR1IKTdkiRHloeWs2m+vt7zqdeueZlwJcIzHaWLr69Dp3l4WCV6j4WVtv+9qpgG31zaWOLtOsu1095Kzd1rBb4Eh/jY/BWZoMInyrDLMrfpzHrrtr4KH0DbVEH728kCxe6CNPb5JPRZ2/TlpbVBx4NVW7yIpx//5LD+FGNZoPrVXxxFbVzeVCWDLBZg0ft9PFxiQU3w3+c2TnU87WlXjYXNfzWD3j6+FltExmZ35zoYKQoAjrDVdAZAdFOW5X7rKx43y/XfkNQz58D3Xah1M3TA5T36VyqqQmri/YAZGc4hsZfrfkUdavOpl7Ne9iWOqxYYMXFdIcr17l85AWp8Czzz6rnUPkjTtvNOwk5ysO5vEVVm5Cnqu57m1B4KfCyXMi6Tfq+0nkWa7IIQJtevSKG219Ymtxg2O2rjfadT4oRf5M+AxC9Y7GOGd+RBru0kz+qimu+cI7v2VtSEaI0sH+DriwaU7ZRWzSCL8L9Us1NJI9BDCwH0qE4G8mgwlr6iYCd3q1+P7InHH77hJ41voQfE+P05il6MnKpFZsmZxsRiwSTZLoCO7lXnbK0MH2xp5WTD5ZUqbGKcUp4DcxfDBgBjt7xUhVN2dLCSP0FFo4tqHN5cW0pmDaYgrXeZJIiTDUFkGAO0epjw403O7a0d4aFtcj3tnZsUNoCaZcofaYPhuvfw763kV5xY+ahnT4PTzZk6s5IGCMFBbdeAkQcirDsLTzRwUD2G0OIZH7Fb+pWG36lTAoIYN9BvwrMAaEcbrCilMJg4p2JhT+BWquQYdxwtsaWwehw7xYVcoNECBxjng3h3qsIqi3d5T8ezUev1Xvh2DR+0zBiXVLEb5aPQZLovquFfC0Pm3OVrIsDzGzmZjRoRLH6f97JTT+tg3lmCbxf9n8rkd5HQI44nGYjpvFd3R4RTSqMRp0Lem4gr63bmJO5zQi565PgTjUTyceOycb+Sud7wMwyMFRouKyssYuAYxNpXbc4PRD0rJ50eN7msdIc/Oxx9DXPq4FAX1Ou8xP1D+qTLI8dZ+bKBEWwj/8y30idruD/inswlhIUvTVodODSjn2UnkOCstKVnu4MAG09uIWTK+y624Wffjvs7g1zD2YGu/S/plMMNgDkVe882Ys5V1ceQOl4maHI+DPzQVxfZwAHEwDHS6eckM9rYfOPAIL8AlLRAnS2RkodTjHMrd2GTMP2U+wFx4nTA+BgXQA0Zn0cLm1S4S4op905tc8vd9qnpoG3uEGSahYWt3cVWHovHJg2EWRX+alxFDOiOnzj8DJVrUTJC7EG9p9cucJMD3qZQPbtV2V9p+p/khGDUW56OZjcWdUz+ImqoSfC5IX10ZdcJagZ7w6S2StmxZfvuLJB4XrzJqf0rs/aeA5TM9N10UZx943xLe3ak/Sf1tEeCMnQQaZpAQblUwVtNRMJfoD1BfsuslARVYIO+QzLhX5X64mon/PtqC3JesupDfWybC4Gu70/f6Tv6w8JBPnf79bN5z6e85nirdwjSzNYkjZkMXDzwVdepX0QtTluBnMbazljV4l/xEYNINQttEcg3q8XvFqWETPBsAyrXP4+9Hd844MGOOsmj71SQb8jmv2lG0dhJHgouyJjfA6V83vNw6MmhXqv90ZFVrc8eQ1uLz+48Q0tI/cJf69670eHYKSjfTBYxA8upkViBo3FiamS8En7MbqPKv8+IFUyrB8LQN8CTd9OQkslzBW+FsjOmGBv5x9AnlVET7NbXn9r0RHzqCwx2ld9sQIjD13p/ezfTcfaRSbRFyemcLTohq2E8qPSPMEGeGwDpoDbMAdLdHL6L5eXKF7ziaTGE+jlW83ZfK5T6X/dgh31Xar/aPMIIX3To/0UWfvk53xFlRWA3F6hvxQg6ylJvNewiWUonhkwyaX+FhQPyBWNV0G4NPLPm17I4GFVlOm1rCz4byeETNOKhmzyt+RDKzVESvxdXEu9SQOYrRiOAS1EI4aNVRoxJMZaxZfQEw8hBFuhb5obKftj1KHbBcm8dsejl9/LOwue7mTCyJP1cnOnIBpwUi4p6PzsFZw0hP+4hYQprco6qmes8L07qYqMcTrooLE++aSVnb5oGQUIqQmfDSrkGdkMxp4mNvc322DupA8gWFrt1PxPeu4tHHF1ZaTThxy9sBVPbBmMWMkOE89wKByL0UJqOVGmUu6BO542lIsrazrAlcagb3+iMIFmI0kX8wFkKtTG5vSV1Aeh6Cy+WWwyp/rbkSsszvLQbw3cZHrYoxjm2+xfNmPtHQ30AlLtD1CVzI8k8CmPEwduuggBVVFccEfd8C2Ra78KODsXLuko42efCbbKFFrLNHhO6pf2mFlA9LG5sn9022nQEa9StGfu8MnHZAjSOrxmi+GJXWidbHf4h29GEYylMwOoA7RcbY/QQgibvzBVxQ3E69L9nCWrenNg6p3dxFXw8bs97a5eNnPgtSB2GBkJLBRdiLAs0UmDwBE0aaMA5o066hI0/zH7S2LSz8k0xuaYwO2Z7yNQzMPdJCT5FqZXO4nDldmDD+9Fp733q5nvw8KM/lYT1h86V6qKjA90AbfwAJgyx/b7vXB7mgLM3xfbZP93RAZKLgbs+id6DbVANPx231RzNfA7VmsJPaUGdqL0T19jHwtFCHPIXL171t5PFQYP0fPdgmRBnHDf/MZMbYOS7Fk6R+AywQysyk1wstd24V+jYC3ef4cews2xTnwcvf6hTOgNw0fxoJBO13I4ntLUqr37HcFzTosP2HaXr6LWzpkv2NMkBdFMY8ePBSVgNinXX79QQmg8h2snT3JvN7ywJLSrTFI0ayZVpRpalgwOk23Jh7yDr7kcKn/lx4N67k9PJzqRWALbLTBlQRYYuQhsCDPm6s5XhYghPfrMd94yBgM7IYnbtExgRA3Szyw6eDTL40TUU6uXqxU68Oz2U/01oH0cAJVYutcSBWeZUDH1LabmK/k2xyvZLHNnturozTIB+5iqeuOtiaMYh9+Thy/vH5MXBlWyOEv4RhKofB6YIt4CPMJszzvzxxgFK3F50djsHMx/qSJOxWhj6bSxTNwswCgnty3xcqVKivBcVFt4jNZtUEXpQR/IgRrE3JdwCeSadDonwGwZe2ia6hbrSF5iT73nfGH/7elVpFX7Uoby7rUAc3Z4AHMV35/ZHR4L0Cqn2PpZ9glMWXOq325IDp8oeDJkDblSifrYWKIAyq5cyh03ZmXMWHcV1+zRyMlxcM70tRJ9uiN2hrtiOqrTe1tPAJENsxK6dfuYTvR1+PSTKVPfKOWryyFb2cf67fUD9KhAr9SuxVQI6g9/07puZQT/t0BvkGKJdyQML7Sfa2/w5ORa+DLDS6FbmXkxn6D+pUxwlHIOZ7Hp8cAqcW7Y9Czw42m0nsEaNEg014iwX7G3vQQFv63LnTTKcij2Cp9L4EHgk07cf5YdlJeOWKZwGovbFSNjJ9cbP0q4XFQKxJ+9XuKT0pC9pPgRpbYA425V6HOZmejlGOLP/Fuc5LmxlNLNwkp7iQDDRWhFL5ZRB7vljsFnPM7Jctb6fid/kIiko8MgZq0UqR16FwWpf1AKRFzLa2rdAzjcK0VW54xt++mHGWAnyDQpWZdh4i//Ywi7ebuUhcJzcV39iJFSSUZ3UsVx/2yIacKSJtn+F05gxOLbBCPwG/uXNMFMUlwinpqbZ9QKP8ZhHnEVvbK+asgPPQDg+TW8wVx4rJoD9Yiv+OzM6W8UMKKZAPz92fgteUmbQ5mqp28N0m+rDpFee9yzeA2Etoik7lBJbNOb+OrU/B+UcAWq2tLa065Ztnc35U4C+G56Om5Q69YF//vgRVNgA5ZMgDT4ekNELTNoYwiOt3l7utT0x9STwuAxPY/8LuO+6rdRjy3SWzGsLCuVA9LvPfh/bWw1VpIhOUPGGkPu5IMq5yeyY+Z41/lfCT934N1nulkRnSrTZkkwS25b5BbJltEdRU0RYg2rwDdOVM4sKBeBQxb0v9KCtOJ/A0BaTB81n8w4tj2h0K8/OMsgNWHBYalToIvVebwtd6BWj3jeAgBJkIj2pe3Uzqv2qD3ACcT/jyjDJqvaAll6UAGWlHZdKMcv9oUR/mtcAUsqMIu9zEWmabQxe+iJqP155GJCwgf/lXF7GB3BwT6z3jhuNggpL83yg7nvsF2360H+kO3zWaq9zE20NcTC46fTvqMdYHJrkzT0YiyzQpiWpKRY+0S048ouh1s5jgnzh0BPrla0zMK+FHbIU7Dxd0R1TXrwpe5mKxDUnB4FEjA0lg9uqkVDzv5rK8/MsM7J9pSaqVMLGLv+kuHuxzhXgnTljt0kKa58/olSitCfP1CCsCFItE8yD38UMQ+iIzWe9NgrzBk7vnMK7IXL8H45P1faOcl2v1Wy1KM7Jv+ca3lYDT2TxDBxY+Fcnu7alxlO2ukm8n1tlRKHMhm+NWiX7q+pQamEmfi0/A5tSG/bo24Ws0XgElFc0tsWLcPy5eahFmO2fUvk6pWyNmWUb6KqcdqfjvvxR1QzwXO6i5Jc5Wdod22vXv9qr4PXY+wk/fAzGprvhJHFooEAAecGLPgoN2fLpFGZMB8qixSShtf9/SpJTxHYX1mNB0JbKwstF+1H6qMRqsav4g/rnSBV17x6s/YolqbO5HJeoa2ejkgDdsIvu613bpw9WC1K3xMtKRd8by+0cUkSRx8nzBO5Ui6wFZ8eRU0WM3qs21tGtbHFxFCTCuCH/IjtTtCL0jh3zUvZE9fsgo/xymaFrMcCyDyYrIt38bLNcdocAI8UJUfRSgImTTH3YuZd35H8dAO2RsRuJ6g771h8Gkw3dLEpZgeMrqAyyEkuWjrdpVhYJ3HpnPiHn+1dgkiIGPnc0FYtvxA6jN+LPUiuwiV69IbXWYo7P7K1OrtjmLHweytCxhDIfbXe0Bx7LAjZD+Q8Vz478zABAdzkk9kd0RoBlOIa/qYxtFos24FhPIa7fjcq0Uc+CfBE2fLUn0vZLqz3oPWyruYU4lKLBT+ei7srbAZra3VKSTwTdSBwiBanF7SvRWzkSHG/1hmHbMzOnIsG9wBmXMjE165tLfXaPiYxPAck0Sq/nadsws8qp+wFDV+5p07O/CcvhK7JTy5iTll/Er+xbFQNTaAuxQ8eexHWwxrNFievvxJvmFdEol49s/MgVm/D1gNMphSP2JqwlN246KFdEF5UP0yknrJtOAc/2Cq+MKreh5LILfjUHJNDGB+WR1U4A98YM3k0issrYTnRt0KcHLjWumjbc0r0tr2FJKJ2o9uIrQWvfJ6aLojJEWSzuh+vvV+s88mbDkdy4bSPYs8M9xpNxZ78g2B4hG2VoUAT3OvIs7Ima9y+62xqfVoLRxItbrzniKbOd+WL9TaKmuU/3E2L4Ad+n+swWOpp1o07Rzx+U36WPMBQWynmwhtLTT6WFMs5wPMlnnvMT/pDE4+fLC8rbmgySQX2itqKty4DyM9P6nEPpUyxjGN8uLNu3gGs2eK7bAhBYlCq7h29SocAR0HkxHNwahxkmsBmjoAFcHuSDEtCFxEkKQLFHNS8WR1Xz2UZZlDWw/7jSGARIuIM0o0JyCs1CNkpnnhdoTs+VbKfrkQLXJ1vBwRivAy84jW+oAgeOac98u/jl3BkjlkHiEtjjgiEfpU2LQroHpbieo2wvKUxY5UvmL14PUsl/RY1IYeg+0jMkh1LyiEF1sNwbSCsb+Xk/ADwgswBy8Jerja09441xR2ObXf9kM1QvxB6BMB2gT2arX16Pi93UcrKOL0MUNyz0k5VckzNQBO6kGXxM6koaCNpkc2Rx5m388xHQxN1WL/CLlzCkG3T+1MnXAdQD9hHbk67kWy1cueKuoili68tGJ15mPD3BGNyAQu/deSh+XU2aTEX14Yox2q7+opM41bQhOjPS+q3cZXWQjuD5F9r1rXdfVHI0P0w6SHswUulZ3Hwp3mtpxoIw2NcuZRDWTqR4ujBGMIlS6HZHx12UTHq8eUzkjEubP3EmTLSREOKAVjwAfrwXmmlTzbOKxhW63VP6ex+258Pq2bPAaE+lN1A0i0nZORrsBv61Nn/liSCg815zqJlzPKkM5YDtr93ZtTjG5OgsYslCAAAADmr4khU4MCdtz46OoMhTcCSjD0bNH7MNakjoJy/pIKZOe6eFjFo7yNF2OXv4UxRamnlnv67yxTCRX6ih6BWoTtiHwI66NeUjzYHdHCoQQKGTDPwrANqyuWgfclc/1hrArVWGm4VRJzPMcTABhZHECznZ7JiILyMKVoVh6T0M+rB4AABFWElGVgAAAE1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAOShgAHAAAAEgAAAESgAgAEAAAAAQAAAWagAwAEAAAAAQAAAQQAAAAAQVNDSUkAAABTY3JlZW5zaG90WE1QIMABAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjI2MDwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4zNTg8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpVc2VyQ29tbWVudD5TY3JlZW5zaG90PC9leGlmOlVzZXJDb21tZW50PgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4K"},4941:(A,e,t)=>{t.d(e,{Z:()=>l});const l=t.p+"assets/images/tools-91886b41cefeef7cc35b5cd21c43d2ba.webp"}}]); \ No newline at end of file diff --git a/assets/js/26ea2a44.81d24c57.js b/assets/js/26ea2a44.81d24c57.js deleted file mode 100644 index 126414a..0000000 --- a/assets/js/26ea2a44.81d24c57.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[1435],{3905:function(A,e,t){t.d(e,{Zo:function(){return u},kt:function(){return p}});var n=t(7294);function l(A,e,t){return e in A?Object.defineProperty(A,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):A[e]=t,A}function o(A,e){var t=Object.keys(A);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(A);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(A,e).enumerable}))),t.push.apply(t,n)}return t}function a(A){for(var e=1;e=0||(l[t]=A[t]);return l}(A,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(A);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(A,t)&&(l[t]=A[t])}return l}var r=n.createContext({}),s=function(A){var e=n.useContext(r),t=e;return A&&(t="function"==typeof A?A(e):a(a({},e),A)),t},u=function(A){var e=s(A.components);return n.createElement(r.Provider,{value:e},A.children)},g={inlineCode:"code",wrapper:function(A){var e=A.children;return n.createElement(n.Fragment,{},e)}},c=n.forwardRef((function(A,e){var t=A.components,l=A.mdxType,o=A.originalType,r=A.parentName,u=i(A,["components","mdxType","originalType","parentName"]),c=s(t),p=l,d=c["".concat(r,".").concat(p)]||c[p]||g[p]||o;return t?n.createElement(d,a(a({ref:e},u),{},{components:t})):n.createElement(d,a({ref:e},u))}));function p(A,e){var t=arguments,l=e&&e.mdxType;if("string"==typeof A||l){var o=t.length,a=new Array(o);a[0]=c;var i={};for(var r in e)hasOwnProperty.call(e,r)&&(i[r]=e[r]);i.originalType=A,i.mdxType="string"==typeof A?A:l,a[1]=i;for(var s=2;s More bytes = longer download times"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"JS parse and compile time"),": more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"JS execution time"),": optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Memory consumption"),": everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!")),(0,o.kt)("h2",{id:"what-is-the-recommended-bundle-size"},"What is the recommended bundle size?"),(0,o.kt)("p",null,"AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly."),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Mr Chao",src:t(3140).Z})),(0,o.kt)("h2",{id:"what-do-we-do-then"},"What do we do then?"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Meh",src:t(4273).Z})),(0,o.kt)("h2",{id:"how-to-start-decreasing-the-bundle-size"},"How to start decreasing the bundle size?"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Measure"),": first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI)\xa0is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Analyze"),": Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.")),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Stonks",src:t(5895).Z})),(0,o.kt)("h2",{id:"breaking-up-the-bundle"},"Breaking up the bundle..."),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Monitor network requests"),": These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Reduce the total dom nodes"),": the less the page needs to render, the less time it takes."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Moving work off the main thread"),": By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Caching"),": Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast")),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Breaking Bad",src:t(2087).Z})),(0,o.kt)("h2",{id:"which-strategies-can-we-adopt"},"Which strategies can we adopt?"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Minification and Dead Code Elimination"),": These processes are often summed up as\xa0minifying\xa0or\xa0uglifying."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Tree shaking"),": Tree shaking is dead code elimination on a project or library. Always try to use deps which support \u201ctree shaking\u201d, Bundlephobia could be your friend in this case."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Code Splitting and Lazy Loading"),": Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Replace/rewrite large dependencies"),": Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example)."),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Feature module import"),": Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).")),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Strategy",src:t(4027).Z})),(0,o.kt)("h2",{id:"useful-tools-to-help-you-reducing-bundle-size"},"Useful tools to help you reducing bundle size"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Lighthouse"),": automated tool for improving the performance, quality, and correctness of your web apps"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Bundlephobia"),": Bundlephobia\xa0helps you find the performance impact of npm packages"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"Webpack Bundle Analyzer"),": analyzes your bundle"),(0,o.kt)("li",{parentName:"ul"},(0,o.kt)("strong",{parentName:"li"},"VS Code"),": Import Cost plugin -> Display import/require package size in the editor")),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"Tools",src:t(3176).Z})),(0,o.kt)("h2",{id:"conclusion"},"Conclusion"),(0,o.kt)("p",null,"Performance cannot be stripped down to a single metric such as bundle size. It would be great!\nUnfortunately there is no single place to measure all of them.I think metrics like the\xa0Core Web Vitals and a general look at bundle size should be considered as a starting point.\nYou will cry... A lot... But don\u2019t give up!"),(0,o.kt)("p",null,(0,o.kt)("img",{alt:"The End",src:t(998).Z})))}p.isMDXComponent=!0},2087:function(A,e){e.Z="data:image/webp;base64,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"},1089:function(A,e,t){e.Z=t.p+"assets/images/bundle_everywhere-12122e37ab5ca6bcdd61061c4dc32fd4.webp"},6138:function(A,e){e.Z="data:image/webp;base64,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FzF4MYb5dPUHhs5cr4W48fbG0YBO8KzHr+LA17KHdZxYMTAAUpcIp1VnAxgo31955bEsLblDFuBy5dlIEDsrWBpK464x+XXAKVkwXRr/6wHYa71OvfyxCA1GDVeOMppgCM+SdSSV+Z4ZOovufzwbrFiK3sBdeld88AOcgwYxb7qpz7mhGS+zB64o5jXBTZSyA0MDXdYQvBoDLJ20qMaLfoJFEqWGz2rC81rxT/bY7gBgPtFqMlpyIMYi4u3Yf1172FNN4/W/xvmZprZO3IbwFq9PQMoJbQXQrwxVYYZwvWGXERPk2Uhlz9gRWmzNaVqkp7mubKLzLAUJ6lcuebTvMqkcxJJymwmZtTUr5Gxvd/HWyOx2AAfyO55mL93wAKh7ARoMajGsPnAMsEfBuYIWIdlP2J4tFsRWmhppa6AQ0cfFbM4a8+RPS2EsWikEK0LhB4GwUWRPGqjgRduFXpKdIIclPO1V8FWUIErCbCAPvTG621JcUfAF9zBHAyEDUplreZZ7sJWMytQpAAkQo1oYHokzkMAkASVnsb09gWQHFj9DRXBQN7QHdbarq61G9+bdIH7RjBlmBo2PkcAwx6UQB2Gs+3fjaO2CxPFOSFo3qLw54OIXVcwaQOoFyZksQpx38MAMv8LX68Pl6brrz7OlB1oNcsJNDiE7+FeHZ/8/I0oZAAAARVhJRlYAAABNTQAqAAAACAABh2kABAAAAAEAAAAaAAAAAAADkoYABwAAABIAAABEoAIABAAAAAEAAAGaoAMABAAAAAEAAAEUAAAAAEFTQ0lJAAAAU2NyZWVuc2hvdFhNUCDAAQAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyI+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj4yNzY8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+NDEwPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6VXNlckNvbW1lbnQ+U2NyZWVuc2hvdDwvZXhpZjpVc2VyQ29tbWVudD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cg=="},4273:function(A,e,t){e.Z=t.p+"assets/images/meh-3b42aead7d2246467fd0101518403734.webp"},3140:function(A,e){e.Z="data:image/webp;base64,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"},5895:function(A,e){e.Z="data:image/webp;base64,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"},4027:function(A,e){e.Z="data:image/webp;base64,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"},998:function(A,e){e.Z="data:image/webp;base64,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"},3176:function(A,e,t){e.Z=t.p+"assets/images/tools-91886b41cefeef7cc35b5cd21c43d2ba.webp"}}]); \ No newline at end of file diff --git a/assets/js/29f10043.0e0358dd.js b/assets/js/29f10043.0e0358dd.js deleted file mode 100644 index ca75637..0000000 --- a/assets/js/29f10043.0e0358dd.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[4622],{9059:function(g){g.exports=JSON.parse('{"allTagsPath":"/blog/tags","slug":"/blog/tags/debugging","name":"debugging","count":1,"permalink":"/blog/tags/debugging"}')}}]); \ No newline at end of file diff --git a/assets/js/29f10043.2832aa9e.js b/assets/js/29f10043.2832aa9e.js new file mode 100644 index 0000000..723c2be --- /dev/null +++ b/assets/js/29f10043.2832aa9e.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[4622],{9059:e=>{e.exports=JSON.parse('{"label":"debugging","permalink":"/blog/tags/debugging","allTagsPath":"/blog/tags","count":1}')}}]); \ No newline at end of file diff --git a/assets/js/2b6f3ca6.50feec02.js b/assets/js/2b6f3ca6.50feec02.js deleted file mode 100644 index 787731b..0000000 --- a/assets/js/2b6f3ca6.50feec02.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[801],{3905:function(e,t,r){r.d(t,{Zo:function(){return l},kt:function(){return f}});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),u=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},l=function(e){var t=u(e.components);return n.createElement(p.Provider,{value:t},e.children)},g={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,p=e.parentName,l=c(e,["components","mdxType","originalType","parentName"]),m=u(r),f=o,s=m["".concat(p,".").concat(f)]||m[f]||g[f]||a;return r?n.createElement(s,i(i({ref:t},l),{},{components:r})):n.createElement(s,i({ref:t},l))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=m;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c.mdxType="string"==typeof e?e:o,i[1]=c;for(var u=2;u{r.d(t,{Zo:()=>g,kt:()=>m});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),l=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},g=function(e){var t=l(e.components);return n.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},s=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,p=e.parentName,g=c(e,["components","mdxType","originalType","parentName"]),s=l(r),m=o,b=s["".concat(p,".").concat(m)]||s[m]||u[m]||a;return r?n.createElement(b,i(i({ref:t},g),{},{components:r})):n.createElement(b,i({ref:t},g))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=s;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c.mdxType="string"==typeof e?e:o,i[1]=c;for(var l=2;l{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>u,frontMatter:()=>a,metadata:()=>c,toc:()=>l});var n=r(7462),o=(r(7294),r(3905));const a={slug:"debugging-go-map-races-in-k8s",authors:["philipp"],tags:["go","debugging","backend"]},i="debugging Go map races in k8s",c={permalink:"/blog/debugging-go-map-races-in-k8s",editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/blog/2022-01-19-map-race-debugging/index.mdx",source:"@site/blog/2022-01-19-map-race-debugging/index.mdx",title:"debugging Go map races in k8s",description:"",date:"2022-01-19T00:00:00.000Z",formattedDate:"January 19, 2022",tags:[{label:"go",permalink:"/blog/tags/go"},{label:"debugging",permalink:"/blog/tags/debugging"},{label:"backend",permalink:"/blog/tags/backend"}],hasTruncateMarker:!1,authors:[{name:"Philipp Mieden",title:"MSc",url:"https://github.com/dreadl0ck",imageURL:"https://github.com/dreadl0ck.png",key:"philipp"}],frontMatter:{slug:"debugging-go-map-races-in-k8s",authors:["philipp"],tags:["go","debugging","backend"]},prevItem:{title:"Impact of 3rd party scripts on performance",permalink:"/blog/impact-of-3rd-party-scripts-on-performance"},nextItem:{title:"Relaunching foomo.org",permalink:"/blog/welcome-back-2021"}},p={authorsImageUrls:[void 0]},l=[],g={toc:l};function u(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},g,r,{components:t,mdxType:"MDXLayout"}))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/2b793916.13262be8.js b/assets/js/2b793916.13262be8.js deleted file mode 100644 index 8ddb5e9..0000000 --- a/assets/js/2b793916.13262be8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[7696],{3905:function(e,t,r){r.d(t,{Zo:function(){return u},kt:function(){return d}});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function s(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var a=n.createContext({}),p=function(e){var t=n.useContext(a),r=t;return e&&(r="function"==typeof e?e(t):s(s({},t),e)),r},u=function(e){var t=p(e.components);return n.createElement(a.Provider,{value:t},e.children)},l={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},f=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,i=e.originalType,a=e.parentName,u=c(e,["components","mdxType","originalType","parentName"]),f=p(r),d=o,m=f["".concat(a,".").concat(d)]||f[d]||l[d]||i;return r?n.createElement(m,s(s({ref:t},u),{},{components:r})):n.createElement(m,s({ref:t},u))}));function d(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=r.length,s=new Array(i);s[0]=f;var c={};for(var a in t)hasOwnProperty.call(t,a)&&(c[a]=t[a]);c.originalType=e,c.mdxType="string"==typeof e?e:o,s[1]=c;for(var p=2;p{r.d(t,{Zo:()=>l,kt:()=>f});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function s(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),p=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},l=function(e){var t=p(e.components);return n.createElement(c.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,s=e.originalType,c=e.parentName,l=a(e,["components","mdxType","originalType","parentName"]),d=p(r),f=o,m=d["".concat(c,".").concat(f)]||d[f]||u[f]||s;return r?n.createElement(m,i(i({ref:t},l),{},{components:r})):n.createElement(m,i({ref:t},l))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var s=r.length,i=new Array(s);i[0]=d;var a={};for(var c in t)hasOwnProperty.call(t,c)&&(a[c]=t[c]);a.originalType=e,a.mdxType="string"==typeof e?e:o,i[1]=a;for(var p=2;p{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>u,frontMatter:()=>s,metadata:()=>a,toc:()=>p});var n=r(7462),o=(r(7294),r(3905));const s={sidebar_label:"k8s",sidebar_position:2},i="k8s kubernetes",a={unversionedId:"devops/k8s",id:"devops/k8s",title:"k8s kubernetes",description:"",source:"@site/docs/devops/k8s.md",sourceDirName:"devops",slug:"/devops/k8s",permalink:"/docs/devops/k8s",draft:!1,editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/docs/devops/k8s.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{sidebar_label:"k8s",sidebar_position:2},sidebar:"devopsSidebar",previous:{title:"Spot Instance Node Pools",permalink:"/docs/devops/kubernetes/spot-instance-node-pools"},next:{title:"Intro",permalink:"/docs/devops/monitoring/intro"}},c={},p=[],l={toc:p};function u(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},l,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"k8s-kubernetes"},"k8s kubernetes"))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/2cb19d2e.7ea38433.js b/assets/js/2cb19d2e.7ea38433.js deleted file mode 100644 index 9d09c3e..0000000 --- a/assets/js/2cb19d2e.7ea38433.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[6579],{3905:function(t,e,n){n.d(e,{Zo:function(){return i},kt:function(){return m}});var o=n(7294);function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function c(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);e&&(o=o.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,o)}return n}function u(t){for(var e=1;e=0||(r[n]=t[n]);return r}(t,e);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(t);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(r[n]=t[n])}return r}var p=o.createContext({}),a=function(t){var e=o.useContext(p),n=e;return t&&(n="function"==typeof t?t(e):u(u({},e),t)),n},i=function(t){var e=a(t.components);return o.createElement(p.Provider,{value:e},t.children)},f={inlineCode:"code",wrapper:function(t){var e=t.children;return o.createElement(o.Fragment,{},e)}},s=o.forwardRef((function(t,e){var n=t.components,r=t.mdxType,c=t.originalType,p=t.parentName,i=l(t,["components","mdxType","originalType","parentName"]),s=a(n),m=r,y=s["".concat(p,".").concat(m)]||s[m]||f[m]||c;return n?o.createElement(y,u(u({ref:e},i),{},{components:n})):o.createElement(y,u({ref:e},i))}));function m(t,e){var n=arguments,r=e&&e.mdxType;if("string"==typeof t||r){var c=n.length,u=new Array(c);u[0]=s;var l={};for(var p in e)hasOwnProperty.call(e,p)&&(l[p]=e[p]);l.originalType=t,l.mdxType="string"==typeof t?t:r,u[1]=l;for(var a=2;a{n.d(e,{Zo:()=>s,kt:()=>m});var o=n(7294);function r(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function c(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);e&&(o=o.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,o)}return n}function l(t){for(var e=1;e=0||(r[n]=t[n]);return r}(t,e);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(t);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(r[n]=t[n])}return r}var a=o.createContext({}),u=function(t){var e=o.useContext(a),n=e;return t&&(n="function"==typeof t?t(e):l(l({},e),t)),n},s=function(t){var e=u(t.components);return o.createElement(a.Provider,{value:e},t.children)},f={inlineCode:"code",wrapper:function(t){var e=t.children;return o.createElement(o.Fragment,{},e)}},i=o.forwardRef((function(t,e){var n=t.components,r=t.mdxType,c=t.originalType,a=t.parentName,s=p(t,["components","mdxType","originalType","parentName"]),i=u(n),m=r,y=i["".concat(a,".").concat(m)]||i[m]||f[m]||c;return n?o.createElement(y,l(l({ref:e},s),{},{components:n})):o.createElement(y,l({ref:e},s))}));function m(t,e){var n=arguments,r=e&&e.mdxType;if("string"==typeof t||r){var c=n.length,l=new Array(c);l[0]=i;var p={};for(var a in e)hasOwnProperty.call(e,a)&&(p[a]=e[a]);p.originalType=t,p.mdxType="string"==typeof t?t:r,l[1]=p;for(var u=2;u{n.r(e),n.d(e,{assets:()=>a,contentTitle:()=>l,default:()=>f,frontMatter:()=>c,metadata:()=>p,toc:()=>u});var o=n(7462),r=(n(7294),n(3905));const c={},l="contentful",p={unversionedId:"projects/cms/contentful",id:"projects/cms/contentful",title:"contentful",description:"https://app.contentful.com is one of the very few not open source tools, that we use / support.",source:"@site/docs/projects/cms/contentful.md",sourceDirName:"projects/cms",slug:"/projects/cms/contentful",permalink:"/docs/projects/cms/contentful",draft:!1,editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/docs/projects/cms/contentful.md",tags:[],version:"current",frontMatter:{},sidebar:"projectsSidebar",previous:{title:"contentserver",permalink:"/docs/projects/cms/contentserver"},next:{title:"gotsrpc",permalink:"/docs/projects/gotsrpc"}},a={},u=[{value:"gocontentful",id:"gocontentful",level:2},{value:"contentfulproxy",id:"contentfulproxy",level:2}],s={toc:u};function f(t){let{components:e,...n}=t;return(0,r.kt)("wrapper",(0,o.Z)({},s,n,{components:e,mdxType:"MDXLayout"}),(0,r.kt)("h1",{id:"contentful"},"contentful"),(0,r.kt)("p",null,(0,r.kt)("a",{parentName:"p",href:"https://app.contentful.com"},"https://app.contentful.com")," is one of the very few not open source tools, that we use / support."),(0,r.kt)("h2",{id:"gocontentful"},"gocontentful"),(0,r.kt)("p",null,(0,r.kt)("a",{parentName:"p",href:"https://github.com/foomo/gocontentful"},"https://github.com/foomo/gocontentful")),(0,r.kt)("p",null,"gocontentful is a command line utility and a go librabry."),(0,r.kt)("h2",{id:"contentfulproxy"},"contentfulproxy"),(0,r.kt)("p",null,(0,r.kt)("a",{parentName:"p",href:"https://github.com/foomo/contentfulproxy"},"https://github.com/foomo/contentfulproxy")),(0,r.kt)("p",null,"Is a caching reserve proxy that allows you to save money, when using contentful."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/2e75142e.01971e13.js b/assets/js/2e75142e.01971e13.js new file mode 100644 index 0000000..f2c96b3 --- /dev/null +++ b/assets/js/2e75142e.01971e13.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[7779],{873:e=>{e.exports=JSON.parse('{"permalink":"/blog/tags/search-engine","page":1,"postsPerPage":10,"totalPages":1,"totalCount":1,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/2e7a3344.48ef0b32.js b/assets/js/2e7a3344.48ef0b32.js new file mode 100644 index 0000000..453de56 --- /dev/null +++ b/assets/js/2e7a3344.48ef0b32.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[9142],{7718:o=>{o.exports=JSON.parse('{"permalink":"/blog/tags/search","page":1,"postsPerPage":10,"totalPages":1,"totalCount":1,"blogDescription":"Blog","blogTitle":"Blog"}')}}]); \ No newline at end of file diff --git a/assets/js/3338c0e0.46001c6b.js b/assets/js/3338c0e0.46001c6b.js new file mode 100644 index 0000000..df2e947 --- /dev/null +++ b/assets/js/3338c0e0.46001c6b.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[5791],{3592:e=>{e.exports=JSON.parse('{"label":"frontend","permalink":"/docs/tags/frontend","allTagsPath":"/docs/tags","count":1,"items":[{"id":"frontend/stack","title":"Stack","description":"Our frontend stack is permanently changing as the underlying eco system does. We are trying to adopt technologies at the \\"Slope of Enlightenment\\" in the hype cycle.","permalink":"/docs/frontend/stack"}]}')}}]); \ No newline at end of file diff --git a/assets/js/3338c0e0.8358d78e.js b/assets/js/3338c0e0.8358d78e.js deleted file mode 100644 index 124a83a..0000000 --- a/assets/js/3338c0e0.8358d78e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[5791],{3592:function(e){e.exports=JSON.parse('{"name":"frontend","permalink":"/docs/tags/frontend","docs":[{"id":"frontend/stack","title":"Stack","description":"Our frontend stack is permanently changing as the underlying eco system does. We are trying to adopt technologies at the \\"Slope of Enlightenment\\" in the hype cycle.","permalink":"/docs/frontend/stack"}],"allTagsPath":"/docs/tags"}')}}]); \ No newline at end of file diff --git a/assets/js/3352bb21.54e6102c.js b/assets/js/3352bb21.54e6102c.js deleted file mode 100644 index 95fb71a..0000000 --- a/assets/js/3352bb21.54e6102c.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[2233],{3905:function(e,r,t){t.d(r,{Zo:function(){return s},kt:function(){return m}});var n=t(7294);function o(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function a(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);r&&(n=n.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,n)}return t}function c(e){for(var r=1;r=0||(o[t]=e[t]);return o}(e,r);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var p=n.createContext({}),u=function(e){var r=n.useContext(p),t=r;return e&&(t="function"==typeof e?e(r):c(c({},r),e)),t},s=function(e){var r=u(e.components);return n.createElement(p.Provider,{value:r},e.children)},f={inlineCode:"code",wrapper:function(e){var r=e.children;return n.createElement(n.Fragment,{},r)}},l=n.forwardRef((function(e,r){var t=e.components,o=e.mdxType,a=e.originalType,p=e.parentName,s=i(e,["components","mdxType","originalType","parentName"]),l=u(t),m=o,d=l["".concat(p,".").concat(m)]||l[m]||f[m]||a;return t?n.createElement(d,c(c({ref:r},s),{},{components:t})):n.createElement(d,c({ref:r},s))}));function m(e,r){var t=arguments,o=r&&r.mdxType;if("string"==typeof e||o){var a=t.length,c=new Array(a);c[0]=l;var i={};for(var p in r)hasOwnProperty.call(r,p)&&(i[p]=r[p]);i.originalType=e,i.mdxType="string"==typeof e?e:o,c[1]=i;for(var u=2;u{t.d(r,{Zo:()=>l,kt:()=>m});var o=t(7294);function n(e,r,t){return r in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function a(e,r){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);r&&(o=o.filter((function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable}))),t.push.apply(t,o)}return t}function c(e){for(var r=1;r=0||(n[t]=e[t]);return n}(e,r);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(n[t]=e[t])}return n}var i=o.createContext({}),p=function(e){var r=o.useContext(i),t=r;return e&&(t="function"==typeof e?e(r):c(c({},r),e)),t},l=function(e){var r=p(e.components);return o.createElement(i.Provider,{value:r},e.children)},u={inlineCode:"code",wrapper:function(e){var r=e.children;return o.createElement(o.Fragment,{},r)}},f=o.forwardRef((function(e,r){var t=e.components,n=e.mdxType,a=e.originalType,i=e.parentName,l=s(e,["components","mdxType","originalType","parentName"]),f=p(t),m=n,d=f["".concat(i,".").concat(m)]||f[m]||u[m]||a;return t?o.createElement(d,c(c({ref:r},l),{},{components:t})):o.createElement(d,c({ref:r},l))}));function m(e,r){var t=arguments,n=r&&r.mdxType;if("string"==typeof e||n){var a=t.length,c=new Array(a);c[0]=f;var s={};for(var i in r)hasOwnProperty.call(r,i)&&(s[i]=r[i]);s.originalType=e,s.mdxType="string"==typeof e?e:n,c[1]=s;for(var p=2;p{t.r(r),t.d(r,{assets:()=>i,contentTitle:()=>c,default:()=>u,frontMatter:()=>a,metadata:()=>s,toc:()=>p});var o=t(7462),n=(t(7294),t(3905));const a={},c="Bookmarks",s={unversionedId:"devops/bookmarks",id:"devops/bookmarks",title:"Bookmarks",description:"- https://k8s.af/",source:"@site/docs/devops/bookmarks.md",sourceDirName:"devops",slug:"/devops/bookmarks",permalink:"/docs/devops/bookmarks",draft:!1,editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/docs/devops/bookmarks.md",tags:[],version:"current",frontMatter:{},sidebar:"devopsSidebar",previous:{title:"Jaeger",permalink:"/docs/devops/monitoring/jaeger"}},i={},p=[],l={toc:p};function u(e){let{components:r,...t}=e;return(0,n.kt)("wrapper",(0,o.Z)({},l,t,{components:r,mdxType:"MDXLayout"}),(0,n.kt)("h1",{id:"bookmarks"},"Bookmarks"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},(0,n.kt)("a",{parentName:"li",href:"https://k8s.af/"},"https://k8s.af/"))))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/35b210c0.461bdda0.js b/assets/js/35b210c0.461bdda0.js new file mode 100644 index 0000000..1fd78e6 --- /dev/null +++ b/assets/js/35b210c0.461bdda0.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[5900],{3905:(e,t,r)=>{r.d(t,{Zo:()=>g,kt:()=>m});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),l=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},g=function(e){var t=l(e.components);return n.createElement(p.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},s=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,p=e.parentName,g=c(e,["components","mdxType","originalType","parentName"]),s=l(r),m=o,b=s["".concat(p,".").concat(m)]||s[m]||u[m]||a;return r?n.createElement(b,i(i({ref:t},g),{},{components:r})):n.createElement(b,i({ref:t},g))}));function m(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=s;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c.mdxType="string"==typeof e?e:o,i[1]=c;for(var l=2;l{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>i,default:()=>u,frontMatter:()=>a,metadata:()=>c,toc:()=>l});var n=r(7462),o=(r(7294),r(3905));const a={slug:"debugging-go-map-races-in-k8s",authors:["philipp"],tags:["go","debugging","backend"]},i="debugging Go map races in k8s",c={permalink:"/blog/debugging-go-map-races-in-k8s",editUrl:"https://github.com/foomo/foomo-docs/tree/main/foomo/blog/2022-01-19-map-race-debugging/index.mdx",source:"@site/blog/2022-01-19-map-race-debugging/index.mdx",title:"debugging Go map races in k8s",description:"",date:"2022-01-19T00:00:00.000Z",formattedDate:"January 19, 2022",tags:[{label:"go",permalink:"/blog/tags/go"},{label:"debugging",permalink:"/blog/tags/debugging"},{label:"backend",permalink:"/blog/tags/backend"}],hasTruncateMarker:!1,authors:[{name:"Philipp Mieden",title:"MSc",url:"https://github.com/dreadl0ck",imageURL:"https://github.com/dreadl0ck.png",key:"philipp"}],frontMatter:{slug:"debugging-go-map-races-in-k8s",authors:["philipp"],tags:["go","debugging","backend"]},prevItem:{title:"Impact of 3rd party scripts on performance",permalink:"/blog/impact-of-3rd-party-scripts-on-performance"},nextItem:{title:"Relaunching foomo.org",permalink:"/blog/welcome-back-2021"}},p={authorsImageUrls:[void 0]},l=[],g={toc:l};function u(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,n.Z)({},g,r,{components:t,mdxType:"MDXLayout"}))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/35b210c0.e1207f0f.js b/assets/js/35b210c0.e1207f0f.js deleted file mode 100644 index 9bafc3e..0000000 --- a/assets/js/35b210c0.e1207f0f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[5900],{3905:function(e,t,r){r.d(t,{Zo:function(){return l},kt:function(){return f}});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var p=n.createContext({}),u=function(e){var t=n.useContext(p),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},l=function(e){var t=u(e.components);return n.createElement(p.Provider,{value:t},e.children)},g={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,p=e.parentName,l=c(e,["components","mdxType","originalType","parentName"]),m=u(r),f=o,s=m["".concat(p,".").concat(f)]||m[f]||g[f]||a;return r?n.createElement(s,i(i({ref:t},l),{},{components:r})):n.createElement(s,i({ref:t},l))}));function f(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=m;var c={};for(var p in t)hasOwnProperty.call(t,p)&&(c[p]=t[p]);c.originalType=e,c.mdxType="string"==typeof e?e:o,i[1]=c;for(var u=2;u{r.d(t,{Zo:()=>p,kt:()=>k});var a=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function n(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var s=a.createContext({}),c=function(e){var t=a.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):n(n({},t),e)),r},p=function(e){var t=c(e.components);return a.createElement(s.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var r=e.components,o=e.mdxType,l=e.originalType,s=e.parentName,p=i(e,["components","mdxType","originalType","parentName"]),m=c(r),k=o,f=m["".concat(s,".").concat(k)]||m[k]||u[k]||l;return r?a.createElement(f,n(n({ref:t},p),{},{components:r})):a.createElement(f,n({ref:t},p))}));function k(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var l=r.length,n=new Array(l);n[0]=m;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i.mdxType="string"==typeof e?e:o,n[1]=i;for(var c=2;c{r.r(t),r.d(t,{contentTitle:()=>n,default:()=>p,frontMatter:()=>l,metadata:()=>i,toc:()=>s});var a=r(7462),o=(r(7294),r(3905));const l={},n="Awesome list of software",i={type:"mdx",permalink:"/awesome-software",source:"@site/src/pages/awesome-software.md",title:"Awesome list of software",description:"A list of commercial and open source software we use, because it is awesome.",frontMatter:{}},s=[{value:"desktop software",id:"desktop-software",level:2},{value:"devtools",id:"devtools",level:3},{value:"IDEs",id:"ides",level:4},{value:"creative tools",id:"creative-tools",level:3},{value:"cli utilities",id:"cli-utilities",level:2},{value:"k8s / docker",id:"k8s--docker",level:2},{value:"backup",id:"backup",level:3},{value:"software as a service",id:"software-as-a-service",level:2},{value:"productivity / collaboration",id:"productivity--collaboration",level:3},{value:"CMS",id:"cms",level:3}],c={toc:s};function p(e){let{components:t,...r}=e;return(0,o.kt)("wrapper",(0,a.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("h1",{id:"awesome-list-of-software"},"Awesome list of software"),(0,o.kt)("p",null,"A list of commercial and open source software we use, because it is awesome."),(0,o.kt)("div",{className:"alert alert--danger",role:"alert"},"Please note, when you are installing software, ",(0,o.kt)("strong",null,"use a package manager")),(0,o.kt)("br",null),(0,o.kt)("h2",{id:"desktop-software"},"desktop software"),(0,o.kt)("h3",{id:"devtools"},"devtools"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"docker for desktop ",(0,o.kt)("a",{parentName:"li",href:"https://www.docker.com/"},"https://www.docker.com/"))),(0,o.kt)("h4",{id:"ides"},"IDEs"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"goland ",(0,o.kt)("a",{parentName:"li",href:"https://www.jetbrains.com/go"},"https://www.jetbrains.com/go")),(0,o.kt)("li",{parentName:"ul"},"vscode ",(0,o.kt)("a",{parentName:"li",href:"https://code.visualstudio.com/"},"https://code.visualstudio.com/"))),(0,o.kt)("h3",{id:"creative-tools"},"creative tools"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"blender ",(0,o.kt)("a",{parentName:"li",href:"https://blender.org"},"https://blender.org"))),(0,o.kt)("h2",{id:"cli-utilities"},"cli utilities"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"git ",(0,o.kt)("a",{parentName:"li",href:"https://git-scm.com/"},"https://git-scm.com/")),(0,o.kt)("li",{parentName:"ul"},"brew ",(0,o.kt)("a",{parentName:"li",href:"https://brew.sh/"},"https://brew.sh/")," # there is no meaningful life on the mac without it")),(0,o.kt)("h2",{id:"k8s--docker"},"k8s / docker"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"docker ",(0,o.kt)("a",{parentName:"li",href:"https://www.docker.com/"},"https://www.docker.com/")),(0,o.kt)("li",{parentName:"ul"},"k9s ",(0,o.kt)("a",{parentName:"li",href:"https://k9scli.io/"},"https://k9scli.io/"))),(0,o.kt)("h3",{id:"backup"},"backup"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"rclone ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/rclone/rclone"},"https://github.com/rclone/rclone")),(0,o.kt)("li",{parentName:"ul"},"restic ",(0,o.kt)("a",{parentName:"li",href:"https://github.com/restic/restic"},"https://github.com/restic/restic"))),(0,o.kt)("h2",{id:"software-as-a-service"},"software as a service"),(0,o.kt)("h3",{id:"productivity--collaboration"},"productivity / collaboration"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"miro ",(0,o.kt)("a",{parentName:"li",href:"https://miro.com"},"https://miro.com")),(0,o.kt)("li",{parentName:"ul"},"slack ",(0,o.kt)("a",{parentName:"li",href:"https://slack.com"},"https://slack.com"))),(0,o.kt)("h3",{id:"cms"},"CMS"),(0,o.kt)("ul",null,(0,o.kt)("li",{parentName:"ul"},"contentful https:// contentful.com"),(0,o.kt)("li",{parentName:"ul"},"strapi ",(0,o.kt)("a",{parentName:"li",href:"https://strapi.com"},"https://strapi.com")),(0,o.kt)("li",{parentName:"ul"},"jamstack ",(0,o.kt)("a",{parentName:"li",href:"https://jamstack.org/"},"https://jamstack.org/")," # good resource, when checking the state of jamstack")))}p.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/36213d62.d07b30c0.js b/assets/js/36213d62.d07b30c0.js deleted file mode 100644 index cf168aa..0000000 --- a/assets/js/36213d62.d07b30c0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[4574],{3905:function(e,t,r){r.d(t,{Zo:function(){return u},kt:function(){return k}});var a=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function n(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function l(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=a.createContext({}),s=function(e){var t=a.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},u=function(e){var t=s(e.components);return a.createElement(c.Provider,{value:t},e.children)},p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var r=e.components,o=e.mdxType,n=e.originalType,c=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),m=s(r),k=o,f=m["".concat(c,".").concat(k)]||m[k]||p[k]||n;return r?a.createElement(f,l(l({ref:t},u),{},{components:r})):a.createElement(f,l({ref:t},u))}));function k(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var n=r.length,l=new Array(n);l[0]=m;var i={};for(var c in t)hasOwnProperty.call(t,c)&&(i[c]=t[c]);i.originalType=e,i.mdxType="string"==typeof e?e:o,l[1]=i;for(var s=2;s{a.r(t),a.d(t,{default:()=>i});var l=a(7294),n=a(6010),r=a(5155),c=a(833),s=a(5281),m=a(7767),o=a(6090),u=a(197);function i(e){let{tags:t}=e;const a=(0,r.M)();return l.createElement(c.FG,{className:(0,n.Z)(s.k.wrapper.docsPages,s.k.page.docsTagsListPage)},l.createElement(c.d,{title:a}),l.createElement(u.Z,{tag:"doc_tags_list"}),l.createElement(m.Z,null,l.createElement("div",{className:"container margin-vert--lg"},l.createElement("div",{className:"row"},l.createElement("main",{className:"col col--8 col--offset-2"},l.createElement("h1",null,a),l.createElement(o.Z,{tags:t}))))))}},3008:(e,t,a)=>{a.d(t,{Z:()=>o});var l=a(7294),n=a(6010),r=a(9960);const c="tag_zVej",s="tagRegular_sFm0",m="tagWithCount_h2kH";function o(e){let{permalink:t,label:a,count:o}=e;return l.createElement(r.Z,{href:t,className:(0,n.Z)(c,o?m:s)},a,o&&l.createElement("span",null,o))}},6090:(e,t,a)=>{a.d(t,{Z:()=>m});var l=a(7294),n=a(5155),r=a(3008);const c="tag_Nnez";function s(e){let{letterEntry:t}=e;return l.createElement("article",null,l.createElement("h2",null,t.letter),l.createElement("ul",{className:"padding--none"},t.tags.map((e=>l.createElement("li",{key:e.permalink,className:c},l.createElement(r.Z,e))))),l.createElement("hr",null))}function m(e){let{tags:t}=e;const a=(0,n.P)(t);return l.createElement("section",{className:"margin-vert--lg"},a.map((e=>l.createElement(s,{key:e.letter,letterEntry:e}))))}},5155:(e,t,a)=>{a.d(t,{M:()=>n,P:()=>r});var l=a(5999);const n=()=>(0,l.I)({id:"theme.tags.tagsPageTitle",message:"Tags",description:"The title of the tag list page"});function r(e){const t={};return Object.values(e).forEach((e=>{const a=function(e){return e[0].toUpperCase()}(e.label);t[a]??=[],t[a].push(e)})),Object.entries(t).sort(((e,t)=>{let[a]=e,[l]=t;return a.localeCompare(l)})).map((e=>{let[t,a]=e;return{letter:t,tags:a.sort(((e,t)=>e.label.localeCompare(t.label)))}}))}}}]); \ No newline at end of file diff --git a/assets/js/3720c009.8226915e.js b/assets/js/3720c009.8226915e.js deleted file mode 100644 index e4ccbcf..0000000 --- a/assets/js/3720c009.8226915e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[3751],{3578:function(e,t,a){a.r(t);var n=a(7294),r=a(8882),l=a(6681),c=a(3306);t.default=function(e){var t=e.tags,a=(0,l.MA)();return n.createElement(r.Z,{title:a,wrapperClassName:l.kM.wrapper.docsPages,pageClassName:l.kM.page.docsTagsListPage,searchMetadata:{tag:"doc_tags_list"}},n.createElement("div",{className:"container margin-vert--lg"},n.createElement("div",{className:"row"},n.createElement("main",{className:"col col--8 col--offset-2"},n.createElement("h1",null,a),n.createElement(c.Z,{tags:t})))))}},7774:function(e,t,a){a.d(t,{Z:function(){return u}});var n=a(7294),r=a(6010),l=a(9960),c="tag_WK-t",s="tagRegular_LXbV",m="tagWithCount_S5Zl";var u=function(e){var t,a=e.permalink,u=e.name,o=e.count;return n.createElement(l.Z,{href:a,className:(0,r.Z)(c,(t={},t[s]=!o,t[m]=o,t))},u,o&&n.createElement("span",null,o))}},3306:function(e,t,a){a.d(t,{Z:function(){return m}});var n=a(7294),r=a(7774),l=a(6681),c="tag_7kA+";function s(e){var t=e.letterEntry;return n.createElement("article",null,n.createElement("h2",null,t.letter),n.createElement("ul",{className:"padding--none"},t.tags.map((function(e){return n.createElement("li",{key:e.permalink,className:c},n.createElement(r.Z,e))}))),n.createElement("hr",null))}var m=function(e){var t=e.tags,a=(0,l.PZ)(t);return n.createElement("section",{className:"margin-vert--lg"},a.map((function(e){return n.createElement(s,{key:e.letter,letterEntry:e})})))}}}]); \ No newline at end of file diff --git a/assets/js/37e97a4e.0b478ffc.js b/assets/js/37e97a4e.0b478ffc.js deleted file mode 100644 index 9b200f9..0000000 --- a/assets/js/37e97a4e.0b478ffc.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkfoomo=self.webpackChunkfoomo||[]).push([[8735],{3905:function(e,t,r){r.d(t,{Zo:function(){return c},kt:function(){return h}});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var l=n.createContext({}),p=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},c=function(e){var t=p(e.components);return n.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,l=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),d=p(r),h=o,m=d["".concat(l,".").concat(h)]||d[h]||u[h]||a;return r?n.createElement(m,i(i({ref:t},c),{},{components:r})):n.createElement(m,i({ref:t},c))}));function h(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=d;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s.mdxType="string"==typeof e?e:o,i[1]=s;for(var p=2;p\n ...\n // include Partytown and set custom path due to multiple frontends\n \n // tag 3rd party script with partytown type\n
-

Awesome list of software

A list of commercial and open source software we use, because it is awesome.


desktop software

devtools

IDEs

creative tools

cli utilities

k8s / docker

backup

software as a service

productivity / collaboration

CMS

- - +

Awesome list of software

A list of commercial and open source software we use, because it is awesome.


desktop software

devtools

IDEs

creative tools

cli utilities

k8s / docker

backup

software as a service

productivity / collaboration

CMS

+ + \ No newline at end of file diff --git a/blog.html b/blog.html index 3bb1156..addce84 100644 --- a/blog.html +++ b/blog.html @@ -1,28 +1,30 @@ - + - - - + +Blog | foomo project docs -Blog | foomo project docs - - + + + + + + - +
-

Nicola Turcato

Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). -So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! +

Nicola Turcato

Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). +So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! Unfortunately there is no single place to measure all of them.I think metrics like the Core Web Vitals and a general look at bundle size should be considered as a starting point. -You will cry... A lot... But don’t give up!

The End

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. And since we're running in kubernetes I'm going to die soon, again and again. And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. @@ -30,15 +32,15 @@ So, as you can see much of life's problems can be accredited to cardinality For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve -https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc -https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

Marko Trebižan

Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. -We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
...
new CopyPlugin({
patterns: [
{
// we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
// paths for SSR and client side rendering differ
to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
},
],
})
);

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
...
// include Partytown and set custom path due to multiple frontends
<Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
// tag 3rd party script with partytown type
<script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
...
</Head>

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

Jan Halfar

A few years ago we abandoned the previous version of https://www.foomo.org as we did not want to maintain the old wordpress installation and the project was only living in README.md in the repos living under https://www.github.com/foomo .

As things have grown over time we have decided to re-launch a website / cross project documentation.

So welcome back and enjoy the view to the past:

blast from the past

- - +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve +https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc +https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

Marko Trebižan

Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. +We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
...
new CopyPlugin({
patterns: [
{
// we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
// paths for SSR and client side rendering differ
to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
},
],
})
);

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
...
// include Partytown and set custom path due to multiple frontends
<Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
// tag 3rd party script with partytown type
<script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
...
</Head>

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

Jan Halfar

A few years ago we abandoned the previous version of https://www.foomo.org as we did not want to maintain the old wordpress installation and the project was only living in README.md in the repos living under https://www.github.com/foomo .

As things have grown over time we have decided to re-launch a website / cross project documentation.

So welcome back and enjoy the view to the past:

blast from the past

+ + \ No newline at end of file diff --git a/blog/archive.html b/blog/archive.html index 1438f62..1062218 100644 --- a/blog/archive.html +++ b/blog/archive.html @@ -1,19 +1,21 @@ - + - - - + +Archive | foomo project docs -Archive | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/blog/atom.xml b/blog/atom.xml index c16c83b..b9e59e5 100644 --- a/blog/atom.xml +++ b/blog/atom.xml @@ -13,14 +13,17 @@ 2022-03-17T00:00:00.000Z - Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). -So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

What is the recommended bundle size?

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! -Unfortunately there is no single place to measure all of them.I think metrics like the Core Web Vitals and a general look at bundle size should be considered as a starting point. -You will cry... A lot... But don’t give up!

The End

]]>
+ Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). +So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! +Unfortunately there is no single place to measure all of them.I think metrics like the Core Web Vitals and a general look at bundle size should be considered as a starting point. +You will cry... A lot... But don’t give up!

The End

]]>
Nicola Turcato https://github.com/nicolaturcato + + + <![CDATA[Prometheus Is Out Of Memory. Again.]]> @@ -28,57 +31,37 @@ You will cry... A lot... But don’t give up!

<link 2022-01-25T00:00:00.000Z

- The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! -Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. -And since we're running in kubernetes I'm going to die soon, again and again. -And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring
-
-prometheus-prometheus-kube-prometheus-prometheus-0       1/2     Running   4          5m
-

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
-       Started:      Wed, 12 Jan 2022 15:12:49 +0100                                                                                                                                                                                                │
-     Last State:     Terminated                                                                                                                                                                                                                     │
-       Reason:       OOMKilled                                                                                                                                                                                                                      │
-       Exit Code:    137                                                                                                                                                                                                                            │
-       Started:      Tue, 11 Jan 2022 17:14:41 +0100                                                                                                                                                                                                │
-       Finished:     Wed, 12 Jan 2022 15:12:47 +0100                                                                                                                                                                                                │
-

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). + The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! +Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. +And since we're running in kubernetes I'm going to die soon, again and again. +And you're getting reports from your colleagues that prometheus is not responding. +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. -Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. -Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. -So, as you can see much of life's problems can be accredited to cardinality.

In short cardinality of your metrics is the combination of all label values per metric. -For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. +Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. +Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. +So, as you can see much of life's problems can be accredited to cardinality.

In short cardinality of your metrics is the combination of all label values per metric. +For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. -Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
-mywebsite.com/wp/admin.php
-mywebsite.com/?utm_source=GUID
-...
-

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. -Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .
-

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
-> Duration: 1h59m59.997s
-> Series: 564171
-> Label names: 285
-> Postings (unique label pairs): 21139
-> Postings entries (total label pairs): 6423664
->
-> ...
->
-> Highest cardinality metric names:
-> 11340 haproxy_server_http_responses_total
-> ...
-

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
]]>
+Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
]]> Stefan Martinov https://github.com/smartinov + + + + + + +
<![CDATA[The never ending search a search engine 2022-01 edition]]> @@ -86,13 +69,17 @@ We need to deal with it, so that our prometheus instance can breathe again. In t 2022-01-20T00:00:00.000Z - While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve -https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc -https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

]]>
+ While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve +https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc +https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

]]>
Jan Halfar https://github.com/janhalfar + + + +
<![CDATA[Impact of 3rd party scripts on performance]]> @@ -100,33 +87,14 @@ We need to deal with it, so that our prometheus instance can breathe again. In t 2022-01-20T00:00:00.000Z - Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. -We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
-      ...
-      new CopyPlugin({
-        patterns: [
-          {
-            // we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
-            from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
-            // paths for SSR and client side rendering differ
-            to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
-          },
-        ],
-      })
-    );
-

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
-    ...
-    // include Partytown and set custom path due to multiple frontends
-    <Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
-    // tag 3rd party script with partytown type
-    <script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
-    ...
-</Head>
-

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

]]>
+ Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. +We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
...
new CopyPlugin({
patterns: [
{
// we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
// paths for SSR and client side rendering differ
to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
},
],
})
);

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
...
// include Partytown and set custom path due to multiple frontends
<Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
// tag 3rd party script with partytown type
<script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
...
</Head>

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

]]>
Marko Trebižan https://github.com/themre + +
<![CDATA[debugging Go map races in k8s]]> @@ -137,6 +105,9 @@ We won't dive into specifics how it works, because they explain it nicely o Philipp Mieden https://github.com/dreadl0ck + + + <![CDATA[Relaunching foomo.org]]> @@ -144,10 +115,11 @@ We won't dive into specifics how it works, because they explain it nicely o 2021-11-12T00:00:00.000Z - A few years ago we abandoned the previous version of https://www.foomo.org as we did not want to maintain the old wordpress installation and the project was only living in README.md in the repos living under https://www.github.com/foomo .

As things have grown over time we have decided to re-launch a website / cross project documentation.

So welcome back and enjoy the view to the past:

blast from the past

]]>
+ A few years ago we abandoned the previous version of https://www.foomo.org as we did not want to maintain the old wordpress installation and the project was only living in README.md in the repos living under https://www.github.com/foomo .

As things have grown over time we have decided to re-launch a website / cross project documentation.

So welcome back and enjoy the view to the past:

blast from the past

]]>
Jan Halfar https://github.com/janhalfar +
\ No newline at end of file diff --git a/blog/debugging-go-map-races-in-k8s.html b/blog/debugging-go-map-races-in-k8s.html index 142e4f0..b98f519 100644 --- a/blog/debugging-go-map-races-in-k8s.html +++ b/blog/debugging-go-map-races-in-k8s.html @@ -1,19 +1,21 @@ - + - - - + +debugging Go map races in k8s | foomo project docs -debugging Go map races in k8s | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/blog/impact-of-3rd-party-scripts-on-performance.html b/blog/impact-of-3rd-party-scripts-on-performance.html index 69ce129..439d5af 100644 --- a/blog/impact-of-3rd-party-scripts-on-performance.html +++ b/blog/impact-of-3rd-party-scripts-on-performance.html @@ -1,20 +1,22 @@ - + - - - + +Impact of 3rd party scripts on performance | foomo project docs -Impact of 3rd party scripts on performance | foomo project docs - - + + + + + + - +
-

Impact of 3rd party scripts on performance

Marko Trebižan

Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. -We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
...
new CopyPlugin({
patterns: [
{
// we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
// paths for SSR and client side rendering differ
to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
},
],
})
);

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
...
// include Partytown and set custom path due to multiple frontends
<Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
// tag 3rd party script with partytown type
<script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
...
</Head>

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

- - +

Impact of 3rd party scripts on performance

Marko Trebižan

Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. +We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
...
new CopyPlugin({
patterns: [
{
// we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
// paths for SSR and client side rendering differ
to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
},
],
})
);

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
...
// include Partytown and set custom path due to multiple frontends
<Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
// tag 3rd party script with partytown type
<script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
...
</Head>

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

+ + \ No newline at end of file diff --git a/blog/prometheus-cardinality-issues.html b/blog/prometheus-cardinality-issues.html index 4fd375d..b6710b5 100644 --- a/blog/prometheus-cardinality-issues.html +++ b/blog/prometheus-cardinality-issues.html @@ -1,25 +1,27 @@ - + - - - + +Prometheus Is Out Of Memory. Again. | foomo project docs -Prometheus Is Out Of Memory. Again. | foomo project docs - - + + + + + + - +
-

Prometheus Is Out Of Memory. Again.

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! +

Prometheus Is Out Of Memory. Again.

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. And since we're running in kubernetes I'm going to die soon, again and again. And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. @@ -27,12 +29,12 @@ So, as you can see much of life's problems can be accredited to cardinality For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
- - +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
+ + \ No newline at end of file diff --git a/blog/rss.xml b/blog/rss.xml index af3c285..7917630 100644 --- a/blog/rss.xml +++ b/blog/rss.xml @@ -7,16 +7,20 @@ Thu, 17 Mar 2022 00:00:00 GMT https://validator.w3.org/feed/docs/rss2.html https://github.com/jpmonette/feed + en <![CDATA[Why bundle size is important?]]> https://www.foomo.org/blog/why-bundle-size-is-important why-bundle-size-is-important Thu, 17 Mar 2022 00:00:00 GMT - Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). -So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

What is the recommended bundle size?

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! -Unfortunately there is no single place to measure all of them.I think metrics like the Core Web Vitals and a general look at bundle size should be considered as a starting point. -You will cry... A lot... But don’t give up!

The End

]]>
+ Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). +So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! +Unfortunately there is no single place to measure all of them.I think metrics like the Core Web Vitals and a general look at bundle size should be considered as a starting point. +You will cry... A lot... But don’t give up!

The End

]]>
+ javascript + bundle + bundle size
<![CDATA[Prometheus Is Out Of Memory. Again.]]> @@ -24,53 +28,33 @@ You will cry... A lot... But don’t give up!

<guidprometheus-cardinality-issues Tue, 25 Jan 2022 00:00:00 GMT - The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! -Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. -And since we're running in kubernetes I'm going to die soon, again and again. -And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring
-
-prometheus-prometheus-kube-prometheus-prometheus-0       1/2     Running   4          5m
-

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
-       Started:      Wed, 12 Jan 2022 15:12:49 +0100                                                                                                                                                                                                │
-     Last State:     Terminated                                                                                                                                                                                                                     │
-       Reason:       OOMKilled                                                                                                                                                                                                                      │
-       Exit Code:    137                                                                                                                                                                                                                            │
-       Started:      Tue, 11 Jan 2022 17:14:41 +0100                                                                                                                                                                                                │
-       Finished:     Wed, 12 Jan 2022 15:12:47 +0100                                                                                                                                                                                                │
-

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). + The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! +Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. +And since we're running in kubernetes I'm going to die soon, again and again. +And you're getting reports from your colleagues that prometheus is not responding. +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. -Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. -Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. -So, as you can see much of life's problems can be accredited to cardinality.

In short cardinality of your metrics is the combination of all label values per metric. -For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. +Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. +Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. +So, as you can see much of life's problems can be accredited to cardinality.

In short cardinality of your metrics is the combination of all label values per metric. +For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. -Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
-mywebsite.com/wp/admin.php
-mywebsite.com/?utm_source=GUID
-...
-

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. -Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .
-

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
-> Duration: 1h59m59.997s
-> Series: 564171
-> Label names: 285
-> Postings (unique label pairs): 21139
-> Postings entries (total label pairs): 6423664
->
-> ...
->
-> Highest cardinality metric names:
-> 11340 haproxy_server_http_responses_total
-> ...
-

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
]]> +Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
]]> + prometheus + cardinality + devops + ops + k8s + oom + memory
<![CDATA[The never ending search a search engine 2022-01 edition]]> @@ -78,9 +62,13 @@ We need to deal with it, so that our prometheus instance can breathe again. In t searching-for-search-engines Thu, 20 Jan 2022 00:00:00 GMT - While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve -https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc -https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

]]>
+ While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve +https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc +https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

]]>
+ search + search-engine + backend + go
<![CDATA[Impact of 3rd party scripts on performance]]> @@ -88,35 +76,19 @@ We need to deal with it, so that our prometheus instance can breathe again. In t impact-of-3rd-party-scripts-on-performance Thu, 20 Jan 2022 00:00:00 GMT - Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. -We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
-      ...
-      new CopyPlugin({
-        patterns: [
-          {
-            // we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
-            from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
-            // paths for SSR and client side rendering differ
-            to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
-          },
-        ],
-      })
-    );
-

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
-    ...
-    // include Partytown and set custom path due to multiple frontends
-    <Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
-    // tag 3rd party script with partytown type
-    <script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
-    ...
-</Head>
-

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

]]>
+ Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. +We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
...
new CopyPlugin({
patterns: [
{
// we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
// paths for SSR and client side rendering differ
to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
},
],
})
);

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
...
// include Partytown and set custom path due to multiple frontends
<Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
// tag 3rd party script with partytown type
<script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
...
</Head>

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

]]>
+ frontend + performance
<![CDATA[debugging Go map races in k8s]]> https://www.foomo.org/blog/debugging-go-map-races-in-k8s debugging-go-map-races-in-k8s Wed, 19 Jan 2022 00:00:00 GMT + go + debugging + backend <![CDATA[Relaunching foomo.org]]> @@ -124,7 +96,8 @@ We won't dive into specifics how it works, because they explain it nicely o welcome-back-2021 Fri, 12 Nov 2021 00:00:00 GMT - A few years ago we abandoned the previous version of https://www.foomo.org as we did not want to maintain the old wordpress installation and the project was only living in README.md in the repos living under https://www.github.com/foomo .

As things have grown over time we have decided to re-launch a website / cross project documentation.

So welcome back and enjoy the view to the past:

blast from the past

]]>
+ A few years ago we abandoned the previous version of https://www.foomo.org as we did not want to maintain the old wordpress installation and the project was only living in README.md in the repos living under https://www.github.com/foomo .

As things have grown over time we have decided to re-launch a website / cross project documentation.

So welcome back and enjoy the view to the past:

blast from the past

]]>
+ foomo
\ No newline at end of file diff --git a/blog/searching-for-search-engines.html b/blog/searching-for-search-engines.html index 7e4209e..9fea8f1 100644 --- a/blog/searching-for-search-engines.html +++ b/blog/searching-for-search-engines.html @@ -1,21 +1,23 @@ - + - - - + +The never ending search a search engine 2022-01 edition | foomo project docs -The never ending search a search engine 2022-01 edition | foomo project docs - - + + + + + + - +
-

The never ending search a search engine 2022-01 edition

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve -https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc -https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

- - +

The never ending search a search engine 2022-01 edition

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve +https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc +https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

+ + \ No newline at end of file diff --git a/blog/tags.html b/blog/tags.html index 294a144..08e949d 100644 --- a/blog/tags.html +++ b/blog/tags.html @@ -1,19 +1,21 @@ - + - - - + +Tags | foomo project docs -Tags | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/blog/tags/backend.html b/blog/tags/backend.html index 8d3aad3..b9f8b1b 100644 --- a/blog/tags/backend.html +++ b/blog/tags/backend.html @@ -1,21 +1,23 @@ - + - - - + +2 posts tagged with "backend" | foomo project docs -2 posts tagged with "backend" | foomo project docs - - + + + + + + - +
-

2 posts tagged with "backend"

View All Tags

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve -https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc -https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

- - +

2 posts tagged with "backend"

View All Tags

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve +https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc +https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

+ + \ No newline at end of file diff --git a/blog/tags/bundle-size.html b/blog/tags/bundle-size.html index b75c8dd..0820907 100644 --- a/blog/tags/bundle-size.html +++ b/blog/tags/bundle-size.html @@ -1,22 +1,24 @@ - + - - - + +One post tagged with "bundle size" | foomo project docs -One post tagged with "bundle size" | foomo project docs - - + + + + + + - +
-

One post tagged with "bundle size"

View All Tags

Nicola Turcato

Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). -So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! +

One post tagged with "bundle size"

View All Tags

Nicola Turcato

Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). +So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! Unfortunately there is no single place to measure all of them.I think metrics like the Core Web Vitals and a general look at bundle size should be considered as a starting point. -You will cry... A lot... But don’t give up!

The End

- - +You will cry... A lot... But don’t give up!

The End

+ + \ No newline at end of file diff --git a/blog/tags/bundle.html b/blog/tags/bundle.html index e998399..0ea7ed2 100644 --- a/blog/tags/bundle.html +++ b/blog/tags/bundle.html @@ -1,22 +1,24 @@ - + - - - + +One post tagged with "bundle" | foomo project docs -One post tagged with "bundle" | foomo project docs - - + + + + + + - +
-

One post tagged with "bundle"

View All Tags

Nicola Turcato

Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). -So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! +

One post tagged with "bundle"

View All Tags

Nicola Turcato

Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). +So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! Unfortunately there is no single place to measure all of them.I think metrics like the Core Web Vitals and a general look at bundle size should be considered as a starting point. -You will cry... A lot... But don’t give up!

The End

- - +You will cry... A lot... But don’t give up!

The End

+ + \ No newline at end of file diff --git a/blog/tags/cardinality.html b/blog/tags/cardinality.html index 38a2de2..2292c7f 100644 --- a/blog/tags/cardinality.html +++ b/blog/tags/cardinality.html @@ -1,25 +1,27 @@ - + - - - + +One post tagged with "cardinality" | foomo project docs -One post tagged with "cardinality" | foomo project docs - - + + + + + + - +
-

One post tagged with "cardinality"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! +

One post tagged with "cardinality"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. And since we're running in kubernetes I'm going to die soon, again and again. And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. @@ -27,12 +29,12 @@ So, as you can see much of life's problems can be accredited to cardinality For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
- - +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
+ + \ No newline at end of file diff --git a/blog/tags/debugging.html b/blog/tags/debugging.html index db9650f..4a59bc1 100644 --- a/blog/tags/debugging.html +++ b/blog/tags/debugging.html @@ -1,19 +1,21 @@ - + - - - + +One post tagged with "debugging" | foomo project docs -One post tagged with "debugging" | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/blog/tags/devops.html b/blog/tags/devops.html index 7c31f76..cb2e07e 100644 --- a/blog/tags/devops.html +++ b/blog/tags/devops.html @@ -1,25 +1,27 @@ - + - - - + +One post tagged with "devops" | foomo project docs -One post tagged with "devops" | foomo project docs - - + + + + + + - +
-

One post tagged with "devops"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! +

One post tagged with "devops"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. And since we're running in kubernetes I'm going to die soon, again and again. And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. @@ -27,12 +29,12 @@ So, as you can see much of life's problems can be accredited to cardinality For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
- - +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
+ + \ No newline at end of file diff --git a/blog/tags/foomo.html b/blog/tags/foomo.html index 167ae09..ce8e6b0 100644 --- a/blog/tags/foomo.html +++ b/blog/tags/foomo.html @@ -1,19 +1,21 @@ - + - - - + +One post tagged with "foomo" | foomo project docs -One post tagged with "foomo" | foomo project docs - - + + + + + + - +
-

One post tagged with "foomo"

View All Tags
- - +

One post tagged with "foomo"

View All Tags

Jan Halfar

A few years ago we abandoned the previous version of https://www.foomo.org as we did not want to maintain the old wordpress installation and the project was only living in README.md in the repos living under https://www.github.com/foomo .

As things have grown over time we have decided to re-launch a website / cross project documentation.

So welcome back and enjoy the view to the past:

blast from the past

+ + \ No newline at end of file diff --git a/blog/tags/frontend.html b/blog/tags/frontend.html index 87d83ea..3382343 100644 --- a/blog/tags/frontend.html +++ b/blog/tags/frontend.html @@ -1,20 +1,22 @@ - + - - - + +One post tagged with "frontend" | foomo project docs -One post tagged with "frontend" | foomo project docs - - + + + + + + - +
-

One post tagged with "frontend"

View All Tags

Marko Trebižan

Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. -We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
...
new CopyPlugin({
patterns: [
{
// we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
// paths for SSR and client side rendering differ
to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
},
],
})
);

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
...
// include Partytown and set custom path due to multiple frontends
<Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
// tag 3rd party script with partytown type
<script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
...
</Head>

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

- - +

One post tagged with "frontend"

View All Tags

Marko Trebižan

Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. +We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
...
new CopyPlugin({
patterns: [
{
// we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
// paths for SSR and client side rendering differ
to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
},
],
})
);

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
...
// include Partytown and set custom path due to multiple frontends
<Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
// tag 3rd party script with partytown type
<script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
...
</Head>

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

+ + \ No newline at end of file diff --git a/blog/tags/go.html b/blog/tags/go.html index a6c974d..51b12a8 100644 --- a/blog/tags/go.html +++ b/blog/tags/go.html @@ -1,21 +1,23 @@ - + - - - + +2 posts tagged with "go" | foomo project docs -2 posts tagged with "go" | foomo project docs - - + + + + + + - +
-

2 posts tagged with "go"

View All Tags

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve -https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc -https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

- - +

2 posts tagged with "go"

View All Tags

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve +https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc +https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

+ + \ No newline at end of file diff --git a/blog/tags/javascript.html b/blog/tags/javascript.html index 82698c6..7f78ffc 100644 --- a/blog/tags/javascript.html +++ b/blog/tags/javascript.html @@ -1,22 +1,24 @@ - + - - - + +One post tagged with "javascript" | foomo project docs -One post tagged with "javascript" | foomo project docs - - + + + + + + - +
-

One post tagged with "javascript"

View All Tags

Nicola Turcato

Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). -So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! +

One post tagged with "javascript"

View All Tags

Nicola Turcato

Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). +So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! Unfortunately there is no single place to measure all of them.I think metrics like the Core Web Vitals and a general look at bundle size should be considered as a starting point. -You will cry... A lot... But don’t give up!

The End

- - +You will cry... A lot... But don’t give up!

The End

+ + \ No newline at end of file diff --git a/blog/tags/k-8-s.html b/blog/tags/k-8-s.html index fc3f880..07f2fa4 100644 --- a/blog/tags/k-8-s.html +++ b/blog/tags/k-8-s.html @@ -1,25 +1,27 @@ - + - - - + +One post tagged with "k8s" | foomo project docs -One post tagged with "k8s" | foomo project docs - - + + + + + + - +
-

One post tagged with "k8s"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! +

One post tagged with "k8s"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. And since we're running in kubernetes I'm going to die soon, again and again. And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. @@ -27,12 +29,12 @@ So, as you can see much of life's problems can be accredited to cardinality For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
- - +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
+ + \ No newline at end of file diff --git a/blog/tags/memory.html b/blog/tags/memory.html index c90cf6a..4122865 100644 --- a/blog/tags/memory.html +++ b/blog/tags/memory.html @@ -1,25 +1,27 @@ - + - - - + +One post tagged with "memory" | foomo project docs -One post tagged with "memory" | foomo project docs - - + + + + + + - +
-

One post tagged with "memory"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! +

One post tagged with "memory"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. And since we're running in kubernetes I'm going to die soon, again and again. And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. @@ -27,12 +29,12 @@ So, as you can see much of life's problems can be accredited to cardinality For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
- - +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
+ + \ No newline at end of file diff --git a/blog/tags/oom.html b/blog/tags/oom.html index 744cc17..28e84d7 100644 --- a/blog/tags/oom.html +++ b/blog/tags/oom.html @@ -1,25 +1,27 @@ - + - - - + +One post tagged with "oom" | foomo project docs -One post tagged with "oom" | foomo project docs - - + + + + + + - +
-

One post tagged with "oom"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! +

One post tagged with "oom"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. And since we're running in kubernetes I'm going to die soon, again and again. And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. @@ -27,12 +29,12 @@ So, as you can see much of life's problems can be accredited to cardinality For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
- - +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
+ + \ No newline at end of file diff --git a/blog/tags/ops.html b/blog/tags/ops.html index d17ec72..8ae34e2 100644 --- a/blog/tags/ops.html +++ b/blog/tags/ops.html @@ -1,25 +1,27 @@ - + - - - + +One post tagged with "ops" | foomo project docs -One post tagged with "ops" | foomo project docs - - + + + + + + - +
-

One post tagged with "ops"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! +

One post tagged with "ops"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. And since we're running in kubernetes I'm going to die soon, again and again. And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. @@ -27,12 +29,12 @@ So, as you can see much of life's problems can be accredited to cardinality For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
- - +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
+ + \ No newline at end of file diff --git a/blog/tags/performance.html b/blog/tags/performance.html index 0171acc..648c9df 100644 --- a/blog/tags/performance.html +++ b/blog/tags/performance.html @@ -1,20 +1,22 @@ - + - - - + +One post tagged with "performance" | foomo project docs -One post tagged with "performance" | foomo project docs - - + + + + + + - +
-

One post tagged with "performance"

View All Tags

Marko Trebižan

Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. -We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
...
new CopyPlugin({
patterns: [
{
// we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
// paths for SSR and client side rendering differ
to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
},
],
})
);

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
...
// include Partytown and set custom path due to multiple frontends
<Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
// tag 3rd party script with partytown type
<script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
...
</Head>

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

- - +

One post tagged with "performance"

View All Tags

Marko Trebižan

Issue with performance

When building an ecommerce site or an application where performance is a great deal for the users, you need to keep your application fast and responsive. Frontend developers have already many use-cases when the UI becomes laggy and this increases when 3rd party scripts are being included, such as Google Tag Manager or various Live chats (e.g. Intercom).

This does not only influences the users when using the site but also Lighthouse score gets lower which also influences page rankings. So the most naive and easy way for this is to defer loading of such scripts but when you need to get all the data from the start of the application, such tactic is not an option. So what else can we do?

Partytown to the rescue

Developers at BuilderIO created an library Partytown that would allow relocating resources from 3rd party scripts off the main thread. +We won't dive into specifics how it works, because they explain it nicely on their GitHub page.

In our stack we use Next.js React framework and we will go through the basic steps that will allow us to include Partytown for Google Tag Manager.

Setup

Partytown script needs to be located inside our application and live on the same domain. Since we're using monorepo structure, we need to copy this script across all our frontend application. For that we used CopyPlugin webpack plugin in our Next.js config file:

config.plugins.push(
...
new CopyPlugin({
patterns: [
{
// we copy script from node_modules partytown package to `~partytown` folder in our package that serves static files
from: path.join(path.dirname(require.resolve('@builder.io/partytown')), 'lib'),
// paths for SSR and client side rendering differ
to: path.join(`${isServer ? '..' : '.'}/static/assets/`, '~partytown'),
},
],
})
);

Partytown's requirement is that it needs to know what script should it load into own web worker. For that we set script type to text/partytown. This will prevent script to load on initial load.

Inside _document.tsx we add this:

<Head>
...
// include Partytown and set custom path due to multiple frontends
<Partytown lib={`${addTrailingSlash(this.props.basePath)}_next/static/assets/~partytown/`} debug={false} />
// tag 3rd party script with partytown type
<script type="text/partytown" src={`https://www.googletagmanager.com/gtm.js?id=${id}`} />
...
</Head>

Results

So now, does it work? We used one of our large Ecommerce sites to test the landing Lighthouse score.

This was before adding Partytown:

Lighthouse before Partytown

Here you can see 2 critical things: almost 1s of total blocking time (TBT) and 9s of time to interactive (TTI).

After we added Partytown, we got this:

Lighthouse after Partytown

Time to interactive went from 9s to 6.1s which is almost 33% improvement and total blocking time was reduce by more than 50%! We were more than impressed how easy it was to improve our performances.

Side note: Both screenshots were compressed using Squoosh App.

Next steps

After successful testing of Partytown for Google Tag Manager script, we are more than interested in trying it out on our other scripts. One important topic will be to test Partytown with other service-worker related libraries and how to use them together.

+ + \ No newline at end of file diff --git a/blog/tags/prometheus.html b/blog/tags/prometheus.html index c9cb6e3..17910a4 100644 --- a/blog/tags/prometheus.html +++ b/blog/tags/prometheus.html @@ -1,25 +1,27 @@ - + - - - + +One post tagged with "prometheus" | foomo project docs -One post tagged with "prometheus" | foomo project docs - - + + + + + + - +
-

One post tagged with "prometheus"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! +

One post tagged with "prometheus"

View All Tags

The Annoyance

So, we've all been there. You go to your trusty grafana, search for some sweet metrics that you implemented and WHAM! Prometheus returns us a 503, a trusty way of saying I'm not ready, and I'm probably going to die soon. And since we're running in kubernetes I'm going to die soon, again and again. And you're getting reports from your colleagues that prometheus is not responding. -And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. -Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). +And you can't ignore them anymore.

Bummer.

The Problem

All right, lets check what's happening to the little guy.

kubectl get pods -n monitoring

prometheus-prometheus-kube-prometheus-prometheus-0 1/2 Running 4 5m

It seems like it's stuck in the running state, where the container is not yet ready. +Let's describe the deployment, to check out what's happening.

     State:          Running                                                                                                                                                                                                                        │
Started: Wed, 12 Jan 2022 15:12:49 +0100 │
Last State: Terminated │
Reason: OOMKilled │
Exit Code: 137 │
Started: Tue, 11 Jan 2022 17:14:41 +0100 │
Finished: Wed, 12 Jan 2022 15:12:47 +0100 │

So we see that the prometheus is in a running state waiting for the readiness probe to trigger, probably working on recovering from Write Ahead Log (WAL). This could be an issue where prometheus is recovering from an error, or a restart and does not have enough memory to write everything in the WAL. -We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. +We could be running into an issue where we set the request/limits memory lower than the prometheus requires, and the kube scheduler keeps killing prometheus for wanting more memory.

For this case, we could give it more memory to work to see if it recovers. We should also analyze why the prometheus WAL is getting clogged up.

In essence, we want to check what has changed so that we suddenly have a high memory spike in our sweet, sweet environment.

The Source

Cardinality

A lot of prometheus issues revolve around cardinality. Memory spikes that break your deployment? Cardinality. Prometheus dragging its feet like it's Monday after the log4j (the second one ofc) zero day security breach? Cardinality. Not getting that raise since you worked hard the past 16 years without wavering? You bet your ass it's cardinality. @@ -27,12 +29,12 @@ So, as you can see much of life's problems can be accredited to cardinality For example, if our metric http_request_total had a label response code, and let's say we support 8 status codes, our cardinality starts off at 8. For good measure we want to record the HTTP verb for the request. We support GET POST PUT HEAD which would put the cardinality to 4*8=32. Now, if someone adds a URL to the metric label (!!VERY BAD IDEA!!, but bare with me now) and we have 2 active pages, we'd have a cardinality of 2*4*8=64. -But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. -Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. +But, imagine someone starts scraping your website for potential vulnerabilities. Imagine all the URLs that will appear, most likely only once.

mywebsite.com/admin.php
mywebsite.com/wp/admin.php
mywebsite.com/?utm_source=GUID
...

This would blow up our cardinality to kingdom come. Like you will be out of memory faster than "a new super awesome Javascript gamechanger framework" is born. +Or to quote user naveen17797 Scientists predict the number of js frameworks may exceed human population by 2020,at that point of time random string generators will be used to name those frameworks.

The point to this story is, be very mindful of how you use labels and cardinality in prometheus, since that will indeed have great impact on your prometheus performance.

The Solution

Since this has never happened to me (never-ever) I found the following solution to be handy. Since we can't get prometheus up and running to utilize PromQL to detect the potential issues, we have to find another way to detect high cardinality. -Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. -We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
- - +Therefore, we might want to get our hands dirty with some kubectl exec -it -n monitoring pods/prometheus-prometheus-kube-prometheus-prometheus-0 -- sh, and run the prometheus tsdb analysis too.

/prometheus $ promtool tsdb analyze .

Which produced the result.

> Block ID: 01FT8E8YY4THHZ2S7C3G04GJMG
> Duration: 1h59m59.997s
> Series: 564171
> Label names: 285
> Postings (unique label pairs): 21139
> Postings entries (total label pairs): 6423664
>
> ...
>
> Highest cardinality metric names:
> 11340 haproxy_server_http_responses_total
> ...

We see the potential issue here, where the haproxy_server_http_responses_total metric is having a super-high cardinality which is growing. +We need to deal with it, so that our prometheus instance can breathe again. In this particular case, the solution was updating the haproxy.

... or burn it, up to you.

Flame Thrower

The Further Reading

  1. WAL Definition
  2. WAL & Checkpoints
  3. Using TSDB
  4. Biggest Metrics
  5. Cardinality
+ + \ No newline at end of file diff --git a/blog/tags/search-engine.html b/blog/tags/search-engine.html index dba5e0c..d4221f4 100644 --- a/blog/tags/search-engine.html +++ b/blog/tags/search-engine.html @@ -1,21 +1,23 @@ - + - - - + +One post tagged with "search-engine" | foomo project docs -One post tagged with "search-engine" | foomo project docs - - + + + + + + - +
-

One post tagged with "search-engine"

View All Tags

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve -https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc -https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

- - +

One post tagged with "search-engine"

View All Tags

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve +https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc +https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

+ + \ No newline at end of file diff --git a/blog/tags/search.html b/blog/tags/search.html index 62343f1..f2e5640 100644 --- a/blog/tags/search.html +++ b/blog/tags/search.html @@ -1,21 +1,23 @@ - + - - - + +One post tagged with "search" | foomo project docs -One post tagged with "search" | foomo project docs - - + + + + + + - +
-

One post tagged with "search"

View All Tags

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve -https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc -https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

- - +

One post tagged with "search"

View All Tags

Jan Halfar

While building this website and integrating https://docsearch.algolia.com and evaluating another solution by a large company in parallel I could not help to search github and the web for the current state of search engines and search related services.

Since I had done the same thing about a year ago, I was surprised to see how quickly things are moving atm.

Algolia

I was blown away by the quality of https://www.algolia.com and I wish it was open source, but I guess, we all have to make a living ;)

To see how awesome a web (search) interface can be check out https://www.lacoste.com/us/#query=red%20jackets%20for%20men

Apart from that the UI/UX of their backend tools is fantastic.

Elastic

When it comes to https://www.elastic.com I am a bit nervous about the future of the licensing, despite the fact, that I understand their motivation. At the same time the https://opensearch.org does not seem to be an ampty threat.

typesense.org

I do not know, who was hiding under a rock, but I had not seen https://typesense.org before and they certainly have a bold claim: "The Open Source Algolia Alternative" / "The Easier To Use ElasticSearch Alternative"

When looking at https://github.com/typesense/typesense/graphs/contributors it seems, that Kishore Nallan has been working on this for a while. Unfourtunately I do not really see a lot of external contributions, C++ does not seem to attract a lot of contribution.

MeiliSearch

This Rust project https://www.meilisearch.com/ seems to be picking up speed and is definetly on the test short list. It is a fresh codebase with siginficant open source contributions and certainly will attract new developers with Rust and a modern architecture.

Go eco system

Obviously we are very interested in Go powered software and there are a few notable projects. ATM I do not see anything elastic or algolia like, that would be really mature.

bleve / bluge

Marty Schoch seems to be the man when it comes down to text indexing libraries in written in Go and bluge seems to be THE library, that is solid and modern, when implementing text search in your Go application.

https://github.com/blevesearch/bleve +https://github.com/blugelabs/bluge // next iteration of bleve

projects using bluge

All bleeding edge afaik atm - but definitely good places to look at bluge usage

https://github.com/prabhatsharma/zinc +https://github.com/mosuka/phalanx

Look ma I made a vector database

Gotta take a look at this one - will report later

https://github.com/semi-technologies/weaviate

+ + \ No newline at end of file diff --git a/blog/welcome-back-2021.html b/blog/welcome-back-2021.html index 892183d..eb778fc 100644 --- a/blog/welcome-back-2021.html +++ b/blog/welcome-back-2021.html @@ -1,19 +1,21 @@ - + - - - + +Relaunching foomo.org | foomo project docs -Relaunching foomo.org | foomo project docs - - + + + + + + - +
-

Relaunching foomo.org

Jan Halfar

A few years ago we abandoned the previous version of https://www.foomo.org as we did not want to maintain the old wordpress installation and the project was only living in README.md in the repos living under https://www.github.com/foomo .

As things have grown over time we have decided to re-launch a website / cross project documentation.

So welcome back and enjoy the view to the past:

blast from the past

- - +

Relaunching foomo.org

Jan Halfar

A few years ago we abandoned the previous version of https://www.foomo.org as we did not want to maintain the old wordpress installation and the project was only living in README.md in the repos living under https://www.github.com/foomo .

As things have grown over time we have decided to re-launch a website / cross project documentation.

So welcome back and enjoy the view to the past:

blast from the past

+ + \ No newline at end of file diff --git a/blog/why-bundle-size-is-important.html b/blog/why-bundle-size-is-important.html index 8503a11..a9c5da2 100644 --- a/blog/why-bundle-size-is-important.html +++ b/blog/why-bundle-size-is-important.html @@ -1,22 +1,24 @@ - + - - - + +Why bundle size is important? | foomo project docs -Why bundle size is important? | foomo project docs - - + + + + + + - +
-

Why bundle size is important?

Nicola Turcato

Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). -So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! +

Why bundle size is important?

Nicola Turcato

Intro

JavaScript is parsed, compiled and executed in the main thread of the browser. Which means that users have to wait for all of this before they can interact with the website.

Frontend performance optimization is critical because it accounts for around 80-90% of user response time (10-20% backend). +So when a user is waiting for a page to load, around 80-90% of the time is due to frontend related code and assets.

Nobody likes waiting…

A study found that if a site takes longer than 4 seconds to load, up to 25% of users would abandon the site.

Sending large JavaScript payloads impacts the speed of your site significantly.

Mazzarri

What is a "bundle"?

Your frontend application needs a bunch of JS files to run. These files can be in the format of internal dependency like the JS files you have written yourself. But they can also be external dependencies you use to build your application.

JS bundling is an optimization technique to reduce the number of server requests for JavaScript files. Bundling accomplishes this by merging multiple JA files together into one file to reduce the number of page requests.

Bundle Everywhere

Performance implications

  • Time to transmit over the network: considering slow connections with some mobile devices, it's possible that your page will not be interactive until it loads.-> More bytes = longer download times
  • JS parse and compile time: more code you load, more the browser must parse.-> JS gets parsed and compiled on the main thread, when the main thread is busy, the page can't respond to user input
  • JS execution time: optimally you will only pack the code that you expect to execute. The more code you want to execute the longer it will take. It's possible that your page won't be interactive until some of this completes.-> JS is also executed on the main thread, if your page runs a lot of code before it's really needed, that also delays your Time to Interactive
  • Memory consumption: everything fills up the space -> code itself, runtime variables, DOM elements created, etc.-> Pages appear slow when it consumes a lot of memory. Memory leaks can cause your page to freeze up completely!!

AS SMALL AS POSSIBLE! I experienced that is not really possible to give a precise answer because each application is different. Generally you want the resources on initial load to be as small as possible, so that you decrease the initial load time, and then load more resources as needed on the fly.

Mr Chao

What do we do then?

Meh

How to start decreasing the bundle size?

  • Measure: first of all you want to measure. The first step is to use Lighthouse and try to understand the results. It will give you a couple of interesting metrics and some tips. Time to interactive (TTI) is a good reflection of your bundle size because your bundle needs to be evaluated entirely before a user can interact with your web app.
  • Analyze: Consists on analyzing the bundle in order to detect critical chunks. A useful tool is Webpack Bundle Analyzer.

Stonks

Breaking up the bundle...

  • Monitor network requests: These happens between our FCP and TTI. As the initial request for data often occurs when our components initially mount.
  • Reduce the total dom nodes: the less the page needs to render, the less time it takes.
  • Moving work off the main thread: By moving heavy computations to a web worker, the computation will be run on a separate thread, and not block the actual rendering of the page
  • Caching: Even if not useful for users on first page landing, caching data, bundles, and assets can make subsequent visits way fast

Breaking Bad

Which strategies can we adopt?

  • Minification and Dead Code Elimination: These processes are often summed up as minifying or uglifying.
  • Tree shaking: Tree shaking is dead code elimination on a project or library. Always try to use deps which support “tree shaking”, Bundlephobia could be your friend in this case.
  • Code Splitting and Lazy Loading: Code splitting consists on taking a collection of modules and remove them from the main JS bundle. Lazy loading means we can load this newly created bundle later on.
  • Replace/rewrite large dependencies: Consider replacing or rewriting libraries that are large in size where you might not need all of its functionalities (Moment.js for example).
  • Feature module import: Check to see if you are using only a feature module of the library that can be imported alone without importing the whole library (Lodash for example).

Strategy

Useful tools to help you reducing bundle size

  • Lighthouse: automated tool for improving the performance, quality, and correctness of your web apps
  • Bundlephobia: Bundlephobia helps you find the performance impact of npm packages
  • Webpack Bundle Analyzer: analyzes your bundle
  • VS Code: Import Cost plugin -> Display import/require package size in the editor

Tools

Conclusion

Performance cannot be stripped down to a single metric such as bundle size. It would be great! Unfortunately there is no single place to measure all of them.I think metrics like the Core Web Vitals and a general look at bundle size should be considered as a starting point. -You will cry... A lot... But don’t give up!

The End

- - +You will cry... A lot... But don’t give up!

The End

+ + \ No newline at end of file diff --git a/docs/backend/go-by-example/map-racing.html b/docs/backend/go-by-example/map-racing.html index 5ddfb27..7555c8b 100644 --- a/docs/backend/go-by-example/map-racing.html +++ b/docs/backend/go-by-example/map-racing.html @@ -1,19 +1,21 @@ - + - - - + +map races | foomo project docs -map races | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/backend/go-by-example/nil-maps.html b/docs/backend/go-by-example/nil-maps.html index 9d0abad..e001ed9 100644 --- a/docs/backend/go-by-example/nil-maps.html +++ b/docs/backend/go-by-example/nil-maps.html @@ -1,20 +1,22 @@ - + - - - + +what you should now about nil maps | foomo project docs -what you should now about nil maps | foomo project docs - - + + + + + + - +
-
- - +
+ + \ No newline at end of file diff --git a/docs/backend/go-by-example/panic-and-recover.html b/docs/backend/go-by-example/panic-and-recover.html index 526684e..d325538 100644 --- a/docs/backend/go-by-example/panic-and-recover.html +++ b/docs/backend/go-by-example/panic-and-recover.html @@ -1,19 +1,21 @@ - + - - - + +panic and recover | foomo project docs -panic and recover | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/backend/go-by-example/ranging.html b/docs/backend/go-by-example/ranging.html index 9b5b83a..7b3d2d6 100644 --- a/docs/backend/go-by-example/ranging.html +++ b/docs/backend/go-by-example/ranging.html @@ -1,20 +1,22 @@ - + - - - + +ranging and looping | foomo project docs -ranging and looping | foomo project docs - - + + + + + + - + - - +
+ + \ No newline at end of file diff --git a/docs/backend/go-by-example/yaml-magic.html b/docs/backend/go-by-example/yaml-magic.html index 94ceded..667b5ae 100644 --- a/docs/backend/go-by-example/yaml-magic.html +++ b/docs/backend/go-by-example/yaml-magic.html @@ -1,20 +1,22 @@ - + - - - + +yaml magic | foomo project docs -yaml magic | foomo project docs - - + + + + + + - + - - +
+ + \ No newline at end of file diff --git a/docs/backend/gograpple.html b/docs/backend/gograpple.html index 579d89c..8d3fc7b 100644 --- a/docs/backend/gograpple.html +++ b/docs/backend/gograpple.html @@ -1,19 +1,21 @@ - + - - - + +Gograpple | foomo project docs -Gograpple | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/backend/intro.html b/docs/backend/intro.html index 626746f..18b8ad7 100644 --- a/docs/backend/intro.html +++ b/docs/backend/intro.html @@ -1,20 +1,22 @@ - + - - - + +building backend services with Go and foomo | foomo project docs -building backend services with Go and foomo | foomo project docs - - + + + + + + - +
-
- - +
+ + \ No newline at end of file diff --git a/docs/backend/rtfm.html b/docs/backend/rtfm.html index e4413fa..c939d29 100644 --- a/docs/backend/rtfm.html +++ b/docs/backend/rtfm.html @@ -1,19 +1,21 @@ - + - - - + +External documentation resources | foomo project docs -External documentation resources | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/backend/setup.html b/docs/backend/setup.html index d3c150d..55b4d51 100644 --- a/docs/backend/setup.html +++ b/docs/backend/setup.html @@ -1,19 +1,21 @@ - + - - - + +setup your workspace | foomo project docs -setup your workspace | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/devops/bookmarks.html b/docs/devops/bookmarks.html index c8e582a..c998470 100644 --- a/docs/devops/bookmarks.html +++ b/docs/devops/bookmarks.html @@ -1,19 +1,21 @@ - + - - - + +Bookmarks | foomo project docs -Bookmarks | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/devops/intro.html b/docs/devops/intro.html index e9f88c8..07b780d 100644 --- a/docs/devops/intro.html +++ b/docs/devops/intro.html @@ -1,19 +1,21 @@ - + - - - + +How we run applications in the cloud | foomo project docs -How we run applications in the cloud | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/devops/k8s.html b/docs/devops/k8s.html index 48a7753..cf4a341 100644 --- a/docs/devops/k8s.html +++ b/docs/devops/k8s.html @@ -1,19 +1,21 @@ - + - - - + +k8s kubernetes | foomo project docs -k8s kubernetes | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/devops/kubernetes/spot-instance-node-pools.html b/docs/devops/kubernetes/spot-instance-node-pools.html index 264c591..00865e5 100644 --- a/docs/devops/kubernetes/spot-instance-node-pools.html +++ b/docs/devops/kubernetes/spot-instance-node-pools.html @@ -1,22 +1,24 @@ - + - - - + +Spot Instance Node Pools | foomo project docs -Spot Instance Node Pools | foomo project docs - - + + + + + + - +
-

Spot Instance Node Pools

Setting up spot instance node pools is a great way to save money on stateless applications

Setting Up Node Taints

Setting Up Pod Toleration

Pod Toleration

DaemonSet Toleration

Don't forget to set the daemonset tolerations

Setting Up Pod Disruption Budgets

Setting up a pod disruption budget is important due to erratic node shutdown possibility. +

Spot Instance Node Pools

Setting up spot instance node pools is a great way to save money on stateless applications

Setting Up Node Taints

Setting Up Pod Toleration

Pod Toleration

DaemonSet Toleration

Don't forget to set the daemonset tolerations

Setting Up Pod Disruption Budgets

Setting up a pod disruption budget is important due to erratic node shutdown possibility. What could happen is that nodes that are hosting the applications start terminating, and our application becomes unresponsive until the application is re-located to another node. -To avoid that situation, for the instances we need to configure pod disruption budget on our helm deployments.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: application-name
spec:
maxAvailable: 50%
selector:
matchLabels:
app: application-name

For more details check out here

Setting Up Termination Handling

Setting Up K8s Cron Shutdown Cleanup

- - +To avoid that situation, for the instances we need to configure pod disruption budget on our helm deployments.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: application-name
spec:
maxAvailable: 50%
selector:
matchLabels:
app: application-name

For more details check out here

Setting Up Termination Handling

Setting Up K8s Cron Shutdown Cleanup

+ + \ No newline at end of file diff --git a/docs/devops/monitoring/grafana.html b/docs/devops/monitoring/grafana.html index a77c0f7..774b19c 100644 --- a/docs/devops/monitoring/grafana.html +++ b/docs/devops/monitoring/grafana.html @@ -1,19 +1,21 @@ - + - - - + +Grafana | foomo project docs -Grafana | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/devops/monitoring/intro.html b/docs/devops/monitoring/intro.html index 41bfeda..6596057 100644 --- a/docs/devops/monitoring/intro.html +++ b/docs/devops/monitoring/intro.html @@ -1,19 +1,21 @@ - + - - - + +Monitoring and Alerting | foomo project docs -Monitoring and Alerting | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/devops/monitoring/jaeger.html b/docs/devops/monitoring/jaeger.html index 14bfd3c..c9c464d 100644 --- a/docs/devops/monitoring/jaeger.html +++ b/docs/devops/monitoring/jaeger.html @@ -1,19 +1,21 @@ - + - - - + +Jaeger | foomo project docs -Jaeger | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/frontend/css.html b/docs/frontend/css.html index a5adfe0..4a87022 100644 --- a/docs/frontend/css.html +++ b/docs/frontend/css.html @@ -1,19 +1,21 @@ - + - - - + +CSS | foomo project docs -CSS | foomo project docs - - + + + + + + - +
-
- - +
+ + \ No newline at end of file diff --git a/docs/frontend/debugging_js.html b/docs/frontend/debugging_js.html index c29d4f8..aec0d47 100644 --- a/docs/frontend/debugging_js.html +++ b/docs/frontend/debugging_js.html @@ -1,19 +1,21 @@ - + - - - + +Debugging in JavaScript | foomo project docs -Debugging in JavaScript | foomo project docs - - + + + + + + - +
-

Debugging in JavaScript

Debugging experience is very important because it can save you hours of depressing search for a bug.

console logging

Probably 95% of devs nowadays still use console.log since it's the most convenient way of outputting something. There are also some nicer ways to output something, especially when having an array of object. In that case it's much nicer to use console.table(array) because it will create a nice table with all fields.

Sometimes a debugger in the browser won't create a breakpoint like you want it or you can't find a source file where you would want. A nice trick is to add a debugger keyword in your code which will instantly stop executing script on that place (but only if you have devtools open).

Bisecting

When you can't find what causes an error, a very common approach is doing bisection. This is simply removing half of the code and see if it still works or not. If it works, then the bug is in the removed code, otherwise start removing other half in existing code.

Chrome/Firefox devtools

Today's browsers have really good devtools and we can't imagine developing without them. And they all nice of helper tricks that ease our development and especially debugging.

... TODO: @themre

- - +

Debugging in JavaScript

Debugging experience is very important because it can save you hours of depressing search for a bug.

console logging

Probably 95% of devs nowadays still use console.log since it's the most convenient way of outputting something. There are also some nicer ways to output something, especially when having an array of object. In that case it's much nicer to use console.table(array) because it will create a nice table with all fields.

Sometimes a debugger in the browser won't create a breakpoint like you want it or you can't find a source file where you would want. A nice trick is to add a debugger keyword in your code which will instantly stop executing script on that place (but only if you have devtools open).

Bisecting

When you can't find what causes an error, a very common approach is doing bisection. This is simply removing half of the code and see if it still works or not. If it works, then the bug is in the removed code, otherwise start removing other half in existing code.

Chrome/Firefox devtools

Today's browsers have really good devtools and we can't imagine developing without them. And they all nice of helper tricks that ease our development and especially debugging.

... TODO: @themre

+ + \ No newline at end of file diff --git a/docs/frontend/intro.html b/docs/frontend/intro.html index 9453d25..3a42128 100644 --- a/docs/frontend/intro.html +++ b/docs/frontend/intro.html @@ -1,19 +1,21 @@ - + - - - + +Frontend Guide | foomo project docs -Frontend Guide | foomo project docs - - + + + + + + - +
-

Frontend Guide

In this section you will find a guide on how to develop frontends with the foomo stack. Please also see the general guide.

Let me share a few thoughts on frontend development from our journey @bestbytes the company behind https://www.foomo.org .

Web development has been a wild ride since the beginning of the www in the mid 1990s. Today we take it as granted, that the default way to build applications is with web technologies.

Not too long ago this seemed absurd, since browsers were meant to share scientific research papers and therefore were extremely limited in their capabilities.

It was the era of experimentation, that turned the web into the application runtime, which it is today. Plugins like Macromedias flash inspired the rich APIs we can use today, when building web applications.

A little history

The current stack is the result of a more than 20 year journey.

Last millenium

The open source eco system was in its infancy, there was no github (is that really open source as it should be 🤔), there was not even git, there were no real web frameworks and agile methodologies were an exotic nerd topic.

Browsers would sometimes render things in a recognisable way and a table was the way to render your layout. Reliable interactivity could only result from a server round trip and calling JavaScript had a good chance to crash your browser.

Security in browsers was a complete nightmare, it was a time, when one tab could access anothers tabs dom 🤯

Early 2000s

As Microsoft had won the browser wars, we were stuck with IE 6 and like others we were building web applications with the limited resources we had. JavaScript frameworks were still

The plugin era / FLASH

As browsers were slow, buggy and inconsistent FLASH presented this amazing way to build highly interactive websites. It was the time of loaders, which people accepted, because amazing experiences awaited them: animated vector graphics (artists went wild ...), games, the first videos, client server communications, sockets and a canvas API.

Also ActionScript especially AS3 felt so superior to JavaScript at the time.

Flex

Flex used the capabilities of the flash player to provide a fantastic developer experience to build RIAs (rich internet applications). The resulting productivity, application performance and features were unparalled in the JavaScript eco system until long after the demise of the flash player.

After "thoughts on flash"

After the release of the iPhone and the beginning of the walled garden era flash was one of the initial victims. Just for the record, I definitely prefer an independent eco system, which the web is at least to a certain extent, but it certainly was not noble motives, that were driving Apple when pushing for HTML5 in their thoughts on flash.

HTML5

At its arrival HTML5 could not deliver on the many promises, that were made around it. Browser compatibilty was a nightmare, JavaScript performance was poor, the eco system was in its infancy.

As we left the flash eco system we stopped by RequireJS and were using BACKBONE.JS as we felt extremely burnt by Adobe, after they had dropped their whole eco system like a hot potatoe once Apple had declared the end of flash.

TypeScript and the wild framework years

Despite the fact, that we were very sceptic about trusting large vendors, we adopted TypeScript right after it was initially released and we have remained happy users of it until the present day.

The early years of the Node.js community were extremely wild, there was jokes about the number of frameworks released per day. The number of languages that compiled to JavaScript exploded and it just seemed impossible to keep up with the changing environment.

2020s

As we wake up today as frontend developers, we do not expect, that we will keep on doing things as we do them today until the end of our careers, but we at least see a chance, that the code base of a project will not be outdated with delivery of a MVP.

- - +

Frontend Guide

In this section you will find a guide on how to develop frontends with the foomo stack. Please also see the general guide.

Let me share a few thoughts on frontend development from our journey @bestbytes the company behind https://www.foomo.org .

Web development has been a wild ride since the beginning of the www in the mid 1990s. Today we take it as granted, that the default way to build applications is with web technologies.

Not too long ago this seemed absurd, since browsers were meant to share scientific research papers and therefore were extremely limited in their capabilities.

It was the era of experimentation, that turned the web into the application runtime, which it is today. Plugins like Macromedias flash inspired the rich APIs we can use today, when building web applications.

A little history

The current stack is the result of a more than 20 year journey.

Last millenium

The open source eco system was in its infancy, there was no github (is that really open source as it should be 🤔), there was not even git, there were no real web frameworks and agile methodologies were an exotic nerd topic.

Browsers would sometimes render things in a recognisable way and a table was the way to render your layout. Reliable interactivity could only result from a server round trip and calling JavaScript had a good chance to crash your browser.

Security in browsers was a complete nightmare, it was a time, when one tab could access anothers tabs dom 🤯

Early 2000s

As Microsoft had won the browser wars, we were stuck with IE 6 and like others we were building web applications with the limited resources we had. JavaScript frameworks were still

The plugin era / FLASH

As browsers were slow, buggy and inconsistent FLASH presented this amazing way to build highly interactive websites. It was the time of loaders, which people accepted, because amazing experiences awaited them: animated vector graphics (artists went wild ...), games, the first videos, client server communications, sockets and a canvas API.

Also ActionScript especially AS3 felt so superior to JavaScript at the time.

Flex

Flex used the capabilities of the flash player to provide a fantastic developer experience to build RIAs (rich internet applications). The resulting productivity, application performance and features were unparalled in the JavaScript eco system until long after the demise of the flash player.

After "thoughts on flash"

After the release of the iPhone and the beginning of the walled garden era flash was one of the initial victims. Just for the record, I definitely prefer an independent eco system, which the web is at least to a certain extent, but it certainly was not noble motives, that were driving Apple when pushing for HTML5 in their thoughts on flash.

HTML5

At its arrival HTML5 could not deliver on the many promises, that were made around it. Browser compatibilty was a nightmare, JavaScript performance was poor, the eco system was in its infancy.

As we left the flash eco system we stopped by RequireJS and were using BACKBONE.JS as we felt extremely burnt by Adobe, after they had dropped their whole eco system like a hot potatoe once Apple had declared the end of flash.

TypeScript and the wild framework years

Despite the fact, that we were very sceptic about trusting large vendors, we adopted TypeScript right after it was initially released and we have remained happy users of it until the present day.

The early years of the Node.js community were extremely wild, there was jokes about the number of frameworks released per day. The number of languages that compiled to JavaScript exploded and it just seemed impossible to keep up with the changing environment.

2020s

As we wake up today as frontend developers, we do not expect, that we will keep on doing things as we do them today until the end of our careers, but we at least see a chance, that the code base of a project will not be outdated with delivery of a MVP.

+ + \ No newline at end of file diff --git a/docs/frontend/performance.html b/docs/frontend/performance.html index 13dc234..bd6b770 100644 --- a/docs/frontend/performance.html +++ b/docs/frontend/performance.html @@ -1,20 +1,22 @@ - + - - - + +Performance | foomo project docs -Performance | foomo project docs - - + + + + + + - +
-

Performance

JS

JS is nowadays extremely fast and yet we have many performance issues. Here you can find few common mistakes that occur in JS that can decrease performance.

Extensive use of .map, .filter

Let's say you have a large list of objects and you would like to filter them and transform them in some form. Usually we do this:

const largeArray = [ .... ]
largeArray.map(obj => transformObj(obj)).filter(omitBadObject)

In the above case we first loop through whole set, transform it and then filter things out. Not only does this goes through all the items twice, but it also first time goes through all the items and then filters them. -One optimization would be to first filter them and then transform them, but ideally we should just use a normal for loop or forEach where you go through items only once.

const finalArray = []
largeArray.forEach(obj => {
if (omitBadObject(obj)) {
finalArray.push(transformObj(obj))
}
})

This code will skip another loop of items.

- - +

Performance

JS

JS is nowadays extremely fast and yet we have many performance issues. Here you can find few common mistakes that occur in JS that can decrease performance.

Extensive use of .map, .filter

Let's say you have a large list of objects and you would like to filter them and transform them in some form. Usually we do this:

const largeArray = [ .... ]
largeArray.map(obj => transformObj(obj)).filter(omitBadObject)

In the above case we first loop through whole set, transform it and then filter things out. Not only does this goes through all the items twice, but it also first time goes through all the items and then filters them. +One optimization would be to first filter them and then transform them, but ideally we should just use a normal for loop or forEach where you go through items only once.

const finalArray = []
largeArray.forEach(obj => {
if (omitBadObject(obj)) {
finalArray.push(transformObj(obj))
}
})

This code will skip another loop of items.

+ + \ No newline at end of file diff --git a/docs/frontend/pitfalls.html b/docs/frontend/pitfalls.html index 597acd0..7e19511 100644 --- a/docs/frontend/pitfalls.html +++ b/docs/frontend/pitfalls.html @@ -1,19 +1,21 @@ - + - - - + +Pitfalls | foomo project docs -Pitfalls | foomo project docs - - + + + + + + - +
-

Pitfalls

This section is very important to avoid dangerous bugs in your code. Of course each of our languages or libraries in our stack has many advantages, but they came with dangerous pitfalls.

JavaScript

This section is probably too small to write all the dangerous things in JavaScript, but we hope developers won't do some crazy things like concatenating numbers and strings and such sort of forbidden things will be catched by TS.

But nevertheless, very often we run into issues that are hard to debug.

Mutation

Mutation in JS is a very common problem and thus leads to dangerous bugs.

A very common mutation occurs in spreading. So if you spread an object and it's deeply nested, you need to know that this will only create a shallow copy. So that means that deeply nested fields will still be referenced by the original object. If you need to do a immutable instance we advice in using either klona or immer.

When doing sorting on the array like [1,2,3].sort(...), this does mutate the original array. Devs are used to create new array with .map or .filter, but this here is not the case. So be careful with that. Also do not forget that objects in the array will have a reference to objects in the original array and thus they also needs to be cloned.

Have in mind that doing immutability costs and sometimes it's better to mutate, but also know that in React if you don't create a new reference, setting state won't react. So always have in mind pros and cons of doing mutation.

typeof

typeof is used for checking type of some variables. So when checking if a variable is an array of an object, we try to use typeof. But we should be very careful since typeof works nicely on simple primitive types like number, string, boolean.

When you try to check whether a variable is an object or an array, you try to use typeof. But both are object. So in this case we should use Array.isArray(variable). But be careful, because also null is an object! If you want to deep dive into this, read here.

Numbers are also fun when it comes to NaN. If you check isNaN with typeof, it's actually a number! So for this use-case you need to use Number.isNaN(myNum).

- - +

Pitfalls

This section is very important to avoid dangerous bugs in your code. Of course each of our languages or libraries in our stack has many advantages, but they came with dangerous pitfalls.

JavaScript

This section is probably too small to write all the dangerous things in JavaScript, but we hope developers won't do some crazy things like concatenating numbers and strings and such sort of forbidden things will be catched by TS.

But nevertheless, very often we run into issues that are hard to debug.

Mutation

Mutation in JS is a very common problem and thus leads to dangerous bugs.

A very common mutation occurs in spreading. So if you spread an object and it's deeply nested, you need to know that this will only create a shallow copy. So that means that deeply nested fields will still be referenced by the original object. If you need to do a immutable instance we advice in using either klona or immer.

When doing sorting on the array like [1,2,3].sort(...), this does mutate the original array. Devs are used to create new array with .map or .filter, but this here is not the case. So be careful with that. Also do not forget that objects in the array will have a reference to objects in the original array and thus they also needs to be cloned.

Have in mind that doing immutability costs and sometimes it's better to mutate, but also know that in React if you don't create a new reference, setting state won't react. So always have in mind pros and cons of doing mutation.

typeof

typeof is used for checking type of some variables. So when checking if a variable is an array of an object, we try to use typeof. But we should be very careful since typeof works nicely on simple primitive types like number, string, boolean.

When you try to check whether a variable is an object or an array, you try to use typeof. But both are object. So in this case we should use Array.isArray(variable). But be careful, because also null is an object! If you want to deep dive into this, read here.

Numbers are also fun when it comes to NaN. If you check isNaN with typeof, it's actually a number! So for this use-case you need to use Number.isNaN(myNum).

+ + \ No newline at end of file diff --git a/docs/frontend/rtfm.html b/docs/frontend/rtfm.html index 78822df..2fd1f7c 100644 --- a/docs/frontend/rtfm.html +++ b/docs/frontend/rtfm.html @@ -1,19 +1,21 @@ - + - - - + +External documentation resources | foomo project docs -External documentation resources | foomo project docs - - + + + + + + - +
-

External documentation resources

Runtime / browser

In general the Mozilla Developer Network - MDN is a great resource, if you want to know, what your browser is capable of.

Once you found out, how great an API is, the question to answer will be "can I use" feature X. https://caniuse.com/ is a great resource to find the answers, when trying to understand, if a browser API / feature is ready to use for your audience.

JavaScript

JavaScript historically has a very bad reputation. Please enjoy the following classic piece of infotainment: https://www.destroyallsoftware.com/talks/wat

As JavaScript has been massively iterated on, the runtimes hav ecome crazy fast and the language keeps on evolving.

The (mostly functional) subset of the language, that is being used in modern JavaScript / TypeScript projects to is highly expressive and reads well.

Please take the time and learn about

  • functional programming
  • variable scope
  • modern JavaScript syntax
    • arrow functions
    • new keywords
    • spreading
    • ...
  • promises

CSS

  • CSS block model
  • flex box

Browser APIs

  • canvas
  • fetch
  • local storage
  • websockets
  • sse server side events
  • built in types

TypeScript

- - +

External documentation resources

Runtime / browser

In general the Mozilla Developer Network - MDN is a great resource, if you want to know, what your browser is capable of.

Once you found out, how great an API is, the question to answer will be "can I use" feature X. https://caniuse.com/ is a great resource to find the answers, when trying to understand, if a browser API / feature is ready to use for your audience.

JavaScript

JavaScript historically has a very bad reputation. Please enjoy the following classic piece of infotainment: https://www.destroyallsoftware.com/talks/wat

As JavaScript has been massively iterated on, the runtimes hav ecome crazy fast and the language keeps on evolving.

The (mostly functional) subset of the language, that is being used in modern JavaScript / TypeScript projects to is highly expressive and reads well.

Please take the time and learn about

  • functional programming
  • variable scope
  • modern JavaScript syntax
    • arrow functions
    • new keywords
    • spreading
    • ...
  • promises

CSS

  • CSS block model
  • flex box

Browser APIs

  • canvas
  • fetch
  • local storage
  • websockets
  • sse server side events
  • built in types

TypeScript

+ + \ No newline at end of file diff --git a/docs/frontend/setup.html b/docs/frontend/setup.html index 5e403a5..4cefd6c 100644 --- a/docs/frontend/setup.html +++ b/docs/frontend/setup.html @@ -1,19 +1,21 @@ - + - - - + +Setup | foomo project docs -Setup | foomo project docs - - + + + + + + - +
-

Frontend setup

How to setup your machine for frontend development

general topics

setting up your IDE

  • vscode

frontend specific

  • yarn / npm / package.json
  • nvm
  • browser extensions
    • preact
    • redux
    • (google analytics)

debugging with emulators / devices

  • xcode
  • android studio

Install software

Mac

First of all install brew from https://brew.sh

brew install nvm
brew cask install iterm2

Linux

Windows

Install Linux ;)

- - +

Frontend setup

How to setup your machine for frontend development

general topics

setting up your IDE

  • vscode

frontend specific

  • yarn / npm / package.json
  • nvm
  • browser extensions
    • preact
    • redux
    • (google analytics)

debugging with emulators / devices

  • xcode
  • android studio

Install software

Mac

First of all install brew from https://brew.sh

brew install nvm
brew cask install iterm2

Linux

Windows

Install Linux ;)

+ + \ No newline at end of file diff --git a/docs/frontend/stack.html b/docs/frontend/stack.html index 246aba5..ba9c40e 100644 --- a/docs/frontend/stack.html +++ b/docs/frontend/stack.html @@ -1,19 +1,21 @@ - + - - - + +Stack | foomo project docs -Stack | foomo project docs - - + + + + + + - +
-

Frontend Stack

Our frontend stack is permanently changing as the underlying eco system does. We are trying to adopt technologies at the "Slope of Enlightenment" in the hype cycle.

Former iterations of our stack were MVC or MVVM frontend frameworks. Since the arrival of React and state mangement libraries like Redux and Zustand we have been building reactive applications.

Reactive frontends are fun to develop and they scale.

TypeScript

Is our language of choice to build frontends for web applications.

We have adopted TypeScript very early, right after its initial public release in 2012. Here is a list of motives:

  • it is a superset JavaScript
  • it does not try to replace JavaScript like many other languages, which compile to JavaScript, it tries to complement and improve it, while staying compatible
  • good type system
  • great tooling

While it seemed to be crazy to trust Microsoft as a friendly open source company, we have not been disappointed so far.

React / Preact

Declarative components and views, fast to develop and fast at runtime.

gotsrpc

Since we are building services in Go and not with Node.js we have created a light weight RPC framework to integrate TypeScript with Go.

When creating TS definitions on the Go side we follow camel case convention of naming fields e.g. lastDateModified instead of LastDateModified.

- - +

Frontend Stack

Our frontend stack is permanently changing as the underlying eco system does. We are trying to adopt technologies at the "Slope of Enlightenment" in the hype cycle.

Former iterations of our stack were MVC or MVVM frontend frameworks. Since the arrival of React and state mangement libraries like Redux and Zustand we have been building reactive applications.

Reactive frontends are fun to develop and they scale.

TypeScript

Is our language of choice to build frontends for web applications.

We have adopted TypeScript very early, right after its initial public release in 2012. Here is a list of motives:

  • it is a superset JavaScript
  • it does not try to replace JavaScript like many other languages, which compile to JavaScript, it tries to complement and improve it, while staying compatible
  • good type system
  • great tooling

While it seemed to be crazy to trust Microsoft as a friendly open source company, we have not been disappointed so far.

React / Preact

Declarative components and views, fast to develop and fast at runtime.

gotsrpc

Since we are building services in Go and not with Node.js we have created a light weight RPC framework to integrate TypeScript with Go.

When creating TS definitions on the Go side we follow camel case convention of naming fields e.g. lastDateModified instead of LastDateModified.

+ + \ No newline at end of file diff --git a/docs/frontend/typescript/objects.html b/docs/frontend/typescript/objects.html index bcda404..60b2309 100644 --- a/docs/frontend/typescript/objects.html +++ b/docs/frontend/typescript/objects.html @@ -1,21 +1,23 @@ - + - - - + +Object declaration syntax in TypeScript | foomo project docs -Object declaration syntax in TypeScript | foomo project docs - - + + + + + + - + + + \ No newline at end of file diff --git a/docs/frontend/typescript/spreading.html b/docs/frontend/typescript/spreading.html index 14f8e90..ea56607 100644 --- a/docs/frontend/typescript/spreading.html +++ b/docs/frontend/typescript/spreading.html @@ -1,173 +1,22 @@ - + - - - + +Spreading | foomo project docs -Spreading | foomo project docs - - + + + + + + - +
-

Spreading

JavaScript spread syntax (...) is a surprisingly powerful construct. It has two main use cases in our applications:

Shallow copying

Spreading creates new instances of objects or array, but we need to be very careful because it only does a shallow copy. -If you have a deeply nested object or array, nested entities will still hold a reference to an original value and hence dangerous bugs can occur.

Copying is needed when doing state changes (either local or state management e.g. Redux).

Live Editor
Result
Loading...

Populating jsx attributes

Live Editor
Result
Loading...
- - +

Spreading

JavaScript spread syntax (...) is a surprisingly powerful construct. It has two main use cases in our applications:

Shallow copying

Spreading creates new instances of objects or array, but we need to be very careful because it only does a shallow copy. +If you have a deeply nested object or array, nested entities will still hold a reference to an original value and hence dangerous bugs can occur.

Copying is needed when doing state changes (either local or state management e.g. Redux).

function () {
// let´s start with a relatively simple object
const foo = {
a: 1,
b: 2,
nested: {
child: "is not a copy",
},
};
// this will create a shallow copy of foo and add a field "c"
const bar = { ...foo, c: 3 };
// please note, that the nested property "nested" is not deeply copied,
// thus changes to this object will also affect foo.nested
bar.nested.child += " <- I told you so";

// .foo has been copied though and changes to it affect the original only
foo.a += 1;
return (
<>
<ul>
<li>
<code>foo.a</code> was incremented by 1 and is now <code>2</code>
</li>
<li>
accessing <code>bar.nested.child</code> also changed{" "}
<code>foo.nested.child</code>
</li>
</ul>
<pre>foo: {JSON.stringify(foo)}</pre>
<ul>
<li>
<code>bar.a</code> has kept its original value <code>1</code>
</li>
<li>
a new property <code>c</code> was added
</li>
</ul>
<pre>bar: {JSON.stringify(bar)}</pre>
</>
);
}

Populating jsx attributes

function App() {
// a simple Component, that will render all fields in props
const Foo = (props) => (
<ul>
{
Object
.keys(props)
.map(key => (
<li>
<code>{key}</code>:{props[key]}
</li>
)
)
}
</ul>
);
const data = {
a: 1,
b: 2,
c: 3
}

const { c, ...omittedObject } = data;
return (
<>
{/*
Each of the `data` fields are spreaded as props
*/}
<p>Spread all props</p>
<Foo {...data}/>

<p>Add additional prop</p>
{/*
After we spread, we also add `d` prop.
*/}
<Foo {...data} d={4}/>

<p>Order of props is important</p>
{/*
We will replace `a` with 10
*/}
<Foo {...data} a={10} />
{/*
But this won't replace original `a` with 10
*/}
<Foo a={10} {...data} />

<p>Omit certain properties</p>
{/*
If you wish to omit certain fields, you need to write fields
that you wish to omit and with `...{newVariableName}` you will create a new
object with that name. In our part it's `omittedObject` name.
*/}
<Foo {...omittedObject} />
</>
);
}
+ + \ No newline at end of file diff --git a/docs/frontend/vscode.html b/docs/frontend/vscode.html index bf2964e..6e67c60 100644 --- a/docs/frontend/vscode.html +++ b/docs/frontend/vscode.html @@ -1,19 +1,21 @@ - + - - - + +vscode | foomo project docs -vscode | foomo project docs - - + + + + + + - +
-
- - +
+ + \ No newline at end of file diff --git a/docs/general/essentials.html b/docs/general/essentials.html index 2eccc17..0fe7180 100644 --- a/docs/general/essentials.html +++ b/docs/general/essentials.html @@ -1,19 +1,21 @@ - + - - - + +(Random) essentials | foomo project docs -(Random) essentials | foomo project docs - - + + + + + + - +
-

(Random) essentials

Markdown

Learn markdown(this is a markdown document 🤓)

Vim

Vim is everywhere so no matter if you ssh into a place or try to edit a file in a container, you are going to need it.

Here is a super minimal cheat sheet:

# edit/create a file
vim path/file

Now the shortcuts:

shortcutfunctionnote
:h<enter>helpa bit overwhelming
:q<enter>quitimmediate instinct after looking at help 🤣
:q!<enter>quit ignoring changes! means, that you actually mean it and can be combined with a lot of commands
:w<enter>write / save file
:x<enter> save and exit
:x!<enter> force save and exit
11Ggo to line 11
$ go to last char in line
dddelete line
x delete char
i enter insert modethat is where you can actually edit stuff - press escape to exit insert mode
<esc>exit insert mode
<ctrl>vvisual blockthen use cursors to edit selection
xcut
ppaste

ssh

allows you to remotely and securely log into computers - man ssh should be available on any shell.

digital ocean has a decent intro into ssh https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys

There is a lot of other tutorials - your typical use cases will be:

  • logging into other computers
  • executing commands on those computers
  • tunneling network connections

Once ssh is set up correctly scp, git and a lot of other programs, that rely on ssh under the covers are fun to use too.

git

https://git-scm.com/doc

github

Yes despite the fact, that m$ bought them, we use them and you have to enable 2fa

Some data formats

yaml

https://yaml.org/ some love it for good a reason, some hate it for another good reason, but atm there is just no way around it.

json

https://www.json.org/json-en.html similar love and hate situation like with yaml - also no way out.

CSV

If you think parsing or serializing CSV is trivial, you have been warned.

https://datatracker.ietf.org/doc/html/rfc4180

protocol buffers

Fast, compact not human readable - not necessary for most use cases

https://developers.google.com/protocol-buffers

xml

https://www.w3.org/TR/REC-xml/ only little love here

- - +

(Random) essentials

Markdown

Learn markdown(this is a markdown document 🤓)

Vim

Vim is everywhere so no matter if you ssh into a place or try to edit a file in a container, you are going to need it.

Here is a super minimal cheat sheet:

# edit/create a file
vim path/file

Now the shortcuts:

shortcutfunctionnote
:h<enter>helpa bit overwhelming
:q<enter>quitimmediate instinct after looking at help 🤣
:q!<enter>quit ignoring changes! means, that you actually mean it and can be combined with a lot of commands
:w<enter>write / save file
:x<enter> save and exit
:x!<enter> force save and exit
11Ggo to line 11
$ go to last char in line
dddelete line
x delete char
i enter insert modethat is where you can actually edit stuff - press escape to exit insert mode
<esc>exit insert mode
<ctrl>vvisual blockthen use cursors to edit selection
xcut
ppaste

ssh

allows you to remotely and securely log into computers - man ssh should be available on any shell.

digital ocean has a decent intro into ssh https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys

There is a lot of other tutorials - your typical use cases will be:

  • logging into other computers
  • executing commands on those computers
  • tunneling network connections

Once ssh is set up correctly scp, git and a lot of other programs, that rely on ssh under the covers are fun to use too.

git

https://git-scm.com/doc

github

Yes despite the fact, that m$ bought them, we use them and you have to enable 2fa

Some data formats

yaml

https://yaml.org/ some love it for good a reason, some hate it for another good reason, but atm there is just no way around it.

json

https://www.json.org/json-en.html similar love and hate situation like with yaml - also no way out.

CSV

If you think parsing or serializing CSV is trivial, you have been warned.

https://datatracker.ietf.org/doc/html/rfc4180

protocol buffers

Fast, compact not human readable - not necessary for most use cases

https://developers.google.com/protocol-buffers

xml

https://www.w3.org/TR/REC-xml/ only little love here

+ + \ No newline at end of file diff --git a/docs/general/intro.html b/docs/general/intro.html index 8a6ba66..6903344 100644 --- a/docs/general/intro.html +++ b/docs/general/intro.html @@ -1,19 +1,21 @@ - + - - - + +General guide | foomo project docs -General guide | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/general/package-managers.html b/docs/general/package-managers.html index a828ade..9928dc6 100644 --- a/docs/general/package-managers.html +++ b/docs/general/package-managers.html @@ -1,19 +1,21 @@ - + - - - + +Package managers | foomo project docs -Package managers | foomo project docs - - + + + + + + - +
-

Package managers

Whenever you can, do not manage software on your computer manually ...

  • comfort
  • versions
  • security

macOS

On the mac https://brew.sh/ has become the de facto standard - we use it to distribute binaries of some of our open source projects as well.

Windows

...

- - +

Package managers

Whenever you can, do not manage software on your computer manually ...

  • comfort
  • versions
  • security

macOS

On the mac https://brew.sh/ has become the de facto standard - we use it to distribute binaries of some of our open source projects as well.

Windows

...

+ + \ No newline at end of file diff --git a/docs/general/security.html b/docs/general/security.html index a2e6663..5585baf 100644 --- a/docs/general/security.html +++ b/docs/general/security.html @@ -1,19 +1,21 @@ - + - - - + +Security | foomo project docs -Security | foomo project docs - - + + + + + + - +
-

Security

TODO: @dreadl0ck knock yourself out

OS / Application setup

  • disable telemetry whereever possible

Keep your data safe

  • encrypt your file system to make sure so that if your computer gets stolen at least your data remain secure
  • make sure to have multiple backups in different places i.e. one backup disk at home and one in the office
- - +

Security

TODO: @dreadl0ck knock yourself out

OS / Application setup

  • disable telemetry whereever possible

Keep your data safe

  • encrypt your file system to make sure so that if your computer gets stolen at least your data remain secure
  • make sure to have multiple backups in different places i.e. one backup disk at home and one in the office
+ + \ No newline at end of file diff --git a/docs/general/setup/computer.html b/docs/general/setup/computer.html index 4a77b70..45eb349 100644 --- a/docs/general/setup/computer.html +++ b/docs/general/setup/computer.html @@ -1,19 +1,21 @@ - + - - - + +Computer | foomo project docs -Computer | foomo project docs - - + + + + + + - +
-

Setting up your computer

This is a general setup guide for your computer. Make sure that you setup your worpkplace properly too.

Start

  • read this document before you start
  • only use a computer from a trustworthy source
  • reset the computer
  • encrypt your harddrive in order to be on the safe side, if a computer get stolen
  • upgrade to the latest version of the OS you want to use

Privacy and security

Follow the instructions on security when you setup and maintain your system.

Install software

Mac

First of all install brew from https://brew.sh

brew cask install iterm2

Linux

Windows

Install Linux ;)

- - +

Setting up your computer

This is a general setup guide for your computer. Make sure that you setup your worpkplace properly too.

Start

  • read this document before you start
  • only use a computer from a trustworthy source
  • reset the computer
  • encrypt your harddrive in order to be on the safe side, if a computer get stolen
  • upgrade to the latest version of the OS you want to use

Privacy and security

Follow the instructions on security when you setup and maintain your system.

Install software

Mac

First of all install brew from https://brew.sh

brew cask install iterm2

Linux

Windows

Install Linux ;)

+ + \ No newline at end of file diff --git a/docs/general/setup/workplace.html b/docs/general/setup/workplace.html index e3f93df..2ad3163 100644 --- a/docs/general/setup/workplace.html +++ b/docs/general/setup/workplace.html @@ -1,19 +1,21 @@ - + - - - + +Workplace | foomo project docs -Workplace | foomo project docs - - + + + + + + - +
-

Setting up your workplace

No matter where you work, you need a proper workplace:

  • ergonomic office chair / desk combination
  • large high pixel density display
  • ergonomic input devices
  • good lighting
  • correct position in the room

Make sure that you take the time to set up your workplace well, your health depends upon it.

- - +

Setting up your workplace

No matter where you work, you need a proper workplace:

  • ergonomic office chair / desk combination
  • large high pixel density display
  • ergonomic input devices
  • good lighting
  • correct position in the room

Make sure that you take the time to set up your workplace well, your health depends upon it.

+ + \ No newline at end of file diff --git a/docs/general/technologies/sse.html b/docs/general/technologies/sse.html new file mode 100644 index 0000000..1e9ecd9 --- /dev/null +++ b/docs/general/technologies/sse.html @@ -0,0 +1,21 @@ + + + + + +SSE Server Sent Events | foomo project docs + + + + + + + + + +
+

SSE Server Sent Events

Server Sent Events SSE are a well established web standard.

They allow a web server to push messages to the client.

I have created a minimal example https://github.com/janhalfar/sse-playground/tree/main/minimal to illustrate the technology.

Minimal Example in action

if you clone https://github.com/janhalfar/sse-playground locally and run go run minimal/main.go you will be able to open http://localhost:8080 :

screenshot of chrome debugging SSE

Minimal server

This server will:

Let´s take a look at the server response with:

curl -v localhost:8080/sse/time

% curl -v localhost:8080/sse/time
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /sse/time HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: text/event-stream
< Date: Fri, 16 Sep 2022 14:49:17 GMT
< Transfer-Encoding: chunked
<
event: time
id: 1
data: 2022-09-16T16:49:17.482796+02:00

event: time
id: 2
data: 2022-09-16T16:49:17.498366+02:00

event: time
id: 3
data: 2022-09-16T16:49:17.514079+02:00

event: time
id: 4
data: 2022-09-16T16:49:17.529639+02:00

event: time
id: 5
data: 2022-09-16T16:49:17.545335+02:00

...

serving the frontend

The first thing the server is to serve a frontend, which is described below:

serve the embedded index.html
loading...

serving time events

serve an event stream of 100 server sent events
loading...

Minimal client

a minimal and incomplete client example
loading...
+ + + + \ No newline at end of file diff --git a/docs/general/utilities/k9s.html b/docs/general/utilities/k9s.html index 6df6262..eedb5f2 100644 --- a/docs/general/utilities/k9s.html +++ b/docs/general/utilities/k9s.html @@ -1,19 +1,21 @@ - + - - - + +k9s | foomo project docs -k9s | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/general/work/general.html b/docs/general/work/general.html index e745b73..b693389 100644 --- a/docs/general/work/general.html +++ b/docs/general/work/general.html @@ -1,19 +1,21 @@ - + - - - + +Working as a software developer | foomo project docs -Working as a software developer | foomo project docs - - + + + + + + - +
-

Working as a software developer

Working as a software developer in a sustainable way is challenging - here are a few observations of healthy habits:

  • do not work too much
  • physical exercise is essential
  • clearly separate between working and not working
  • reflect and iterate on your working habits
- - +

Working as a software developer

Working as a software developer in a sustainable way is challenging - here are a few observations of healthy habits:

  • do not work too much
  • physical exercise is essential
  • clearly separate between working and not working
  • reflect and iterate on your working habits
+ + \ No newline at end of file diff --git a/docs/general/work/remote-work.html b/docs/general/work/remote-work.html index 2003206..16efaf9 100644 --- a/docs/general/work/remote-work.html +++ b/docs/general/work/remote-work.html @@ -1,19 +1,21 @@ - + - - - + +Working remotely | foomo project docs -Working remotely | foomo project docs - - + + + + + + - +
-

Working remotely

Especially since the beginning of the corona pandemy working remotely has become a very important topic. The foomo team has extensive experience working remotely as we are a distributed team.

Disclaimer: we are almost exclusively working in one timezone.

Requirements

  • fast and reliable internet connection
  • a proper workplace

The good parts

  • no commute
  • less distractions from team members

The bad parts

  • harder to separate between work and your private life
  • less informal communication with teammates
  • less human interaction

When working from home

  • make sure, that you have a routine, that still takes you to work
    • fixed working hours
    • "go to work" and "leave work" even if you do not leave your home
    • if possible use a room, that is for work only
    • have clear rules for your family / roomates, when at work
  • when communication with teammates use video calls
  • take time for your teammates and have a virtual coffee break with someone at least once a day
  • invest half the time, that you save by not communiting to exercise
- - +

Working remotely

Especially since the beginning of the corona pandemy working remotely has become a very important topic. The foomo team has extensive experience working remotely as we are a distributed team.

Disclaimer: we are almost exclusively working in one timezone.

Requirements

  • fast and reliable internet connection
  • a proper workplace

The good parts

  • no commute
  • less distractions from team members

The bad parts

  • harder to separate between work and your private life
  • less informal communication with teammates
  • less human interaction

When working from home

  • make sure, that you have a routine, that still takes you to work
    • fixed working hours
    • "go to work" and "leave work" even if you do not leave your home
    • if possible use a room, that is for work only
    • have clear rules for your family / roomates, when at work
  • when communication with teammates use video calls
  • take time for your teammates and have a virtual coffee break with someone at least once a day
  • invest half the time, that you save by not communiting to exercise
+ + \ No newline at end of file diff --git a/docs/project-management/intro.html b/docs/project-management/intro.html index 72f0a4e..a4ca8b8 100644 --- a/docs/project-management/intro.html +++ b/docs/project-management/intro.html @@ -1,19 +1,21 @@ - + - - - + +Project Managament | foomo project docs -Project Managament | foomo project docs - - + + + + + + - +
-

Project Managament

We scrumming in our way ...

Values

  • project managers manage projects not people

Epics

Epics are the starting point ....

- - +

Project Managament

We scrumming in our way ...

Values

  • project managers manage projects not people

Epics

Epics are the starting point ....

+ + \ No newline at end of file diff --git a/docs/project-management/responsibility.html b/docs/project-management/responsibility.html index a33be99..fbb39d9 100644 --- a/docs/project-management/responsibility.html +++ b/docs/project-management/responsibility.html @@ -1,19 +1,21 @@ - + - - - + +Responsibility | foomo project docs -Responsibility | foomo project docs - - + + + + + + - + - - +

Responsibility

Taking responsibility for a project ...

+ + \ No newline at end of file diff --git a/docs/projects/architecture.html b/docs/projects/architecture.html index 41b5b76..deba688 100644 --- a/docs/projects/architecture.html +++ b/docs/projects/architecture.html @@ -1,19 +1,21 @@ - + - - - + +Architecture overview | foomo project docs -Architecture overview | foomo project docs - - + + + + + + - +
-

Architecture overview

backend services

When it comes to writing backend services we have decided for Go https://go.dev - let me list the main reasons why - Go is:

  • simple and fun
  • fast when compiling
  • fast at runtime
  • friendly to your machines
  • friendly to you as a programmer
  • equipped with a top notch runtime
  • extremely well balanced in its design as a language, that is highly consumable for humans and machines
  • not about the features it has
  • about what has been left out
  • easy to read
  • sustainable

Foomo projects supporting development with Go

  • keel - opinionated way to run services
  • gotsrpc - rpc framework / code generator
  • gograpple - human friendly way to debug go programs running in k8s
  • webgrapple - a development proxy

Frontends

Foomo projects supporting development with Next.js

- - +

Architecture overview

backend services

When it comes to writing backend services we have decided for Go https://go.dev - let me list the main reasons why - Go is:

  • simple and fun
  • fast when compiling
  • fast at runtime
  • friendly to your machines
  • friendly to you as a programmer
  • equipped with a top notch runtime
  • extremely well balanced in its design as a language, that is highly consumable for humans and machines
  • not about the features it has
  • about what has been left out
  • easy to read
  • sustainable

Foomo projects supporting development with Go

  • keel - opinionated way to run services
  • gotsrpc - rpc framework / code generator
  • gograpple - human friendly way to debug go programs running in k8s
  • webgrapple - a development proxy

Frontends

Foomo projects supporting development with Next.js

+ + \ No newline at end of file diff --git a/docs/projects/cms/contentful.html b/docs/projects/cms/contentful.html index 39d5140..8231d9e 100644 --- a/docs/projects/cms/contentful.html +++ b/docs/projects/cms/contentful.html @@ -1,19 +1,21 @@ - + - - - + +contentful | foomo project docs -contentful | foomo project docs - - + + + + + + - +
-
- - +
+ + \ No newline at end of file diff --git a/docs/projects/cms/contentserver.html b/docs/projects/cms/contentserver.html index 4bb39df..25200ce 100644 --- a/docs/projects/cms/contentserver.html +++ b/docs/projects/cms/contentserver.html @@ -1,19 +1,21 @@ - + - - - + +contentserver | foomo project docs -contentserver | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/projects/cms/intro.html b/docs/projects/cms/intro.html index 5788892..bad9e7d 100644 --- a/docs/projects/cms/intro.html +++ b/docs/projects/cms/intro.html @@ -1,19 +1,21 @@ - + - - - + +Intro | foomo project docs -Intro | foomo project docs - - + + + + + + - +
-

Intro

The foomo team has extensive experience with a wide range of CMS systems. Our long journey in the field has brought us very close to the https://jamstack.org . We almost exclusively work with https://app.contentful.com/ but we are watching others like https://www.stripe.com/ very closely.

Expectations and challenges

  • application developers want an environment, that ensures maximum productivity, they do not think of semantic structures, they think of routes
  • customers expect maximum control and flexibility when working with content and do not distinguish between content and applications

The need for dynamic rendering

If you can - render static content and distribute it with a CDN.

There are use cases though, where static site generation does not work like

  • highly personalized content
  • mixing content with entities from other systems, that have a different life cycle, like products

Application routes vs semantic URL structures

Bridging the conceptual gap between sematic content and applications.

Content especially when created with a focus on SEO comes as a semantic graph. This typically conflicts with how application developers see the world.

e-commerce as an example

Let us take a look at a real world example:

application route / ingressapp developers viewSEO URL requirement
/content:id/content/1/
/content/2/mens
/category:id/category/1/mens/shirts
/category/2/mens/shirts/business
/product:id/product/1/mens-shirt-awesome-blue-medium
/store:id/store/1/mens/stores/london-perfect-shirts
/store/2/kids/stores/lego-paradise

our approach

contentserver allows you to resolve URIs to mime types, which can be handled by applications.

Let us return to our ecommerce example.

model content

mime typecms entityapplication eg k8s service
application/x-pagepagehttps://frontend_service_page
application/x-categorycategoryhttps://frontend_service_category
application/x-storestorehttps://frontend_service_store

export contentent to contentserver

contentserver allows you to export your content tree as json

resolving URIs

Use standard application routing whereever possible, since it is the most efficient option.

Once all standard URI resolution fails, query contentserver and it will return the resolved node


Now you have the mime-type and you know which application can handle it.

- - +

Intro

The foomo team has extensive experience with a wide range of CMS systems. Our long journey in the field has brought us very close to the https://jamstack.org . We almost exclusively work with https://app.contentful.com/ but we are watching others like https://www.stripe.com/ very closely.

Expectations and challenges

  • application developers want an environment, that ensures maximum productivity, they do not think of semantic structures, they think of routes
  • customers expect maximum control and flexibility when working with content and do not distinguish between content and applications

The need for dynamic rendering

If you can - render static content and distribute it with a CDN.

There are use cases though, where static site generation does not work like

  • highly personalized content
  • mixing content with entities from other systems, that have a different life cycle, like products

Application routes vs semantic URL structures

Bridging the conceptual gap between sematic content and applications.

Content especially when created with a focus on SEO comes as a semantic graph. This typically conflicts with how application developers see the world.

e-commerce as an example

Let us take a look at a real world example:

application route / ingressapp developers viewSEO URL requirement
/content:id/content/1/
/content/2/mens
/category:id/category/1/mens/shirts
/category/2/mens/shirts/business
/product:id/product/1/mens-shirt-awesome-blue-medium
/store:id/store/1/mens/stores/london-perfect-shirts
/store/2/kids/stores/lego-paradise

our approach

contentserver allows you to resolve URIs to mime types, which can be handled by applications.

Let us return to our ecommerce example.

model content

mime typecms entityapplication eg k8s service
application/x-pagepagehttps://frontend_service_page
application/x-categorycategoryhttps://frontend_service_category
application/x-storestorehttps://frontend_service_store

export contentent to contentserver

contentserver allows you to export your content tree as json

resolving URIs

Use standard application routing whereever possible, since it is the most efficient option.

Once all standard URI resolution fails, query contentserver and it will return the resolved node


Now you have the mime-type and you know which application can handle it.

+ + \ No newline at end of file diff --git a/docs/projects/gotsrpc.html b/docs/projects/gotsrpc.html index d10cd9e..554a009 100644 --- a/docs/projects/gotsrpc.html +++ b/docs/projects/gotsrpc.html @@ -1,19 +1,21 @@ - + - - - + +gotsrpc | foomo project docs -gotsrpc | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/projects/intro.html b/docs/projects/intro.html index b40baec..0d46ecd 100644 --- a/docs/projects/intro.html +++ b/docs/projects/intro.html @@ -1,19 +1,21 @@ - + - - - + +foomo project overview | foomo project docs -foomo project overview | foomo project docs - - + + + + + + - +
-

foomo project overview

"Stuff we wrote, because nobody else wanted to"

All foomo projects are available here https://github.com/foomo also see architecture

We are using the best industry standards and only fill in our own bits, when necessary.

The foomo project maintains libraries, utilities and daemons, which power most projects at https://www.bestbytes.com

Projects running on the foomo stack typically are

  • relatively large with hundreds of thousands of lines of code
  • composed of dozens of microservices
  • written / maintained by dozens of contributors
  • using Go on the server side
  • using Next.js / TypeScript on the client side
  • running on k8s
  • deeply integrated with a headless CMS
  • built with a project specific frontend component set

Everything is loosely coupled, since we know, that the only constant is change.

- - +

foomo project overview

"Stuff we wrote, because nobody else wanted to"

All foomo projects are available here https://github.com/foomo also see architecture

We are using the best industry standards and only fill in our own bits, when necessary.

The foomo project maintains libraries, utilities and daemons, which power most projects at https://www.bestbytes.com

Projects running on the foomo stack typically are

  • relatively large with hundreds of thousands of lines of code
  • composed of dozens of microservices
  • written / maintained by dozens of contributors
  • using Go on the server side
  • using Next.js / TypeScript on the client side
  • running on k8s
  • deeply integrated with a headless CMS
  • built with a project specific frontend component set

Everything is loosely coupled, since we know, that the only constant is change.

+ + \ No newline at end of file diff --git a/docs/projects/libraries/keel.html b/docs/projects/libraries/keel.html index b855d20..dcc99d9 100644 --- a/docs/projects/libraries/keel.html +++ b/docs/projects/libraries/keel.html @@ -1,19 +1,21 @@ - + - - - + +keel | foomo project docs -keel | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/projects/pagespeed-exporter.html b/docs/projects/pagespeed-exporter.html index 48b1c84..04daa77 100644 --- a/docs/projects/pagespeed-exporter.html +++ b/docs/projects/pagespeed-exporter.html @@ -1,19 +1,21 @@ - + - - - + +pagespeed exporter | foomo project docs -pagespeed exporter | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/projects/site-reliability.html b/docs/projects/site-reliability.html index 302e970..17fb3c4 100644 --- a/docs/projects/site-reliability.html +++ b/docs/projects/site-reliability.html @@ -1,19 +1,21 @@ - + - - - + +Site reliability | foomo project docs -Site reliability | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/projects/utilities/gograpple.html b/docs/projects/utilities/gograpple.html index 026d526..3b1efe8 100644 --- a/docs/projects/utilities/gograpple.html +++ b/docs/projects/utilities/gograpple.html @@ -1,19 +1,21 @@ - + - - - + +gograpple | foomo project docs -gograpple | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/projects/webgrapple.html b/docs/projects/webgrapple.html index 80c06a7..ed5d81a 100644 --- a/docs/projects/webgrapple.html +++ b/docs/projects/webgrapple.html @@ -1,19 +1,21 @@ - + - - - + +webgrapple | foomo project docs -webgrapple | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/tags.html b/docs/tags.html index 8f77acf..8634e95 100644 --- a/docs/tags.html +++ b/docs/tags.html @@ -1,19 +1,21 @@ - + - - - + +Tags | foomo project docs -Tags | foomo project docs - - + + + + + + - + - - + + + \ No newline at end of file diff --git a/docs/tags/frontend.html b/docs/tags/frontend.html index 5176ca2..ee3d74b 100644 --- a/docs/tags/frontend.html +++ b/docs/tags/frontend.html @@ -1,19 +1,21 @@ - + - - - + +One doc tagged with "frontend" | foomo project docs -One doc tagged with "frontend" | foomo project docs - - + + + + + + - +
-

One doc tagged with "frontend"

View All Tags

Stack

Our frontend stack is permanently changing as the underlying eco system does. We are trying to adopt technologies at the "Slope of Enlightenment" in the hype cycle.

- - +

One doc tagged with "frontend"

View All Tags

Stack

Our frontend stack is permanently changing as the underlying eco system does. We are trying to adopt technologies at the "Slope of Enlightenment" in the hype cycle.

+ + \ No newline at end of file diff --git a/docs/tags/go.html b/docs/tags/go.html new file mode 100644 index 0000000..03b7e41 --- /dev/null +++ b/docs/tags/go.html @@ -0,0 +1,21 @@ + + + + + +One doc tagged with "go" | foomo project docs + + + + + + + + + +
+

One doc tagged with "go"

View All Tags
+ + + + \ No newline at end of file diff --git a/docs/tags/javascript.html b/docs/tags/javascript.html new file mode 100644 index 0000000..1fa31a7 --- /dev/null +++ b/docs/tags/javascript.html @@ -0,0 +1,21 @@ + + + + + +One doc tagged with "javascript" | foomo project docs + + + + + + + + + +
+

One doc tagged with "javascript"

View All Tags
+ + + + \ No newline at end of file diff --git a/docs/tags/overview.html b/docs/tags/overview.html index ceceea1..78b5d06 100644 --- a/docs/tags/overview.html +++ b/docs/tags/overview.html @@ -1,19 +1,21 @@ - + - - - + +One doc tagged with "overview" | foomo project docs -One doc tagged with "overview" | foomo project docs - - + + + + + + - +
-

One doc tagged with "overview"

View All Tags

Stack

Our frontend stack is permanently changing as the underlying eco system does. We are trying to adopt technologies at the "Slope of Enlightenment" in the hype cycle.

- - +

One doc tagged with "overview"

View All Tags

Stack

Our frontend stack is permanently changing as the underlying eco system does. We are trying to adopt technologies at the "Slope of Enlightenment" in the hype cycle.

+ + \ No newline at end of file diff --git a/docs/tags/sse.html b/docs/tags/sse.html new file mode 100644 index 0000000..3b67e4f --- /dev/null +++ b/docs/tags/sse.html @@ -0,0 +1,21 @@ + + + + + +One doc tagged with "sse" | foomo project docs + + + + + + + + + +
+

One doc tagged with "sse"

View All Tags
+ + + + \ No newline at end of file diff --git a/etc/imprint.html b/etc/imprint.html index f0be421..24693ba 100644 --- a/etc/imprint.html +++ b/etc/imprint.html @@ -1,26 +1,28 @@ - + - - - + +Imprint | foomo project docs -foomo project docs - - + + + + + + - +
-

Imprint

foomo.org is a project of bestbytes

Here is the german info and legalese:

bestbytes GmbH
+

Imprint

foomo.org is a project of bestbytes

Here is the german info and legalese:

bestbytes GmbH
Pienzenauerstr. 10
81679 München
Germany

Telefon: (+49) 089 - 307 62 399
E-Mail: info@bestbytes.de
UST-ID: DE203071105
Vertreten durch die Geschäftsführung

Sven Böttcher
-Hans Halfar

Haftung für Inhalte

Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.

Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.

Quellenangaben: Disclaimer eRecht24 - https://www.e-recht24.de/muster-disclaimer.html

Website-Design, Texte, Grafiken und Layout Copyright © 1998 - today bestbytes. ALLE RECHTE VORBEHALTEN. Das Kopieren oder die Reproduktion der gesamten Website bzw. von Teilen dieser Website ist untersagt, es sei denn, bestbytes hat dem vorher schriftlich oder elektronisch zugestimmt. Alle Markenzeichen und Logos, die auf dieser Internetseite verwendet werden, sind urheberrechtlich geschützt.

- - +Hans Halfar

Haftung für Inhalte

Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.

Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.

Quellenangaben: Disclaimer eRecht24 - https://www.e-recht24.de/muster-disclaimer.html

Website-Design, Texte, Grafiken und Layout Copyright © 1998 - today bestbytes. ALLE RECHTE VORBEHALTEN. Das Kopieren oder die Reproduktion der gesamten Website bzw. von Teilen dieser Website ist untersagt, es sei denn, bestbytes hat dem vorher schriftlich oder elektronisch zugestimmt. Alle Markenzeichen und Logos, die auf dieser Internetseite verwendet werden, sind urheberrechtlich geschützt.

+ + \ No newline at end of file diff --git a/index.html b/index.html index 8d4c0ff..707c083 100644 --- a/index.html +++ b/index.html @@ -1,19 +1,21 @@ - + - - - + +Hello from foomo project docs | foomo project docs -Hello from foomo project docs | foomo project docs - - + + + + + + - +
-

foomo project docs

Stuff we wrote, because nobody else wanted to


foomo is an open source project, that has been maintained by the bestbytes team since 2011. It provides a wide range of utilities, libraries and daemons, that help us to tackle challenging projects.

foomo can help, if you are

  • using Go to write services
  • building frontends with TypeScript and Next.js
  • running your software on k8s
  • looking for a solution to deeply integrate your frontends with a headless cms
If more than two points apply, it will actually help a LOT

General guides

no matter if frontend or backend - there is somtehing for everyone in here

Frontend guides

Build frontends with TypeScript, Next.js, styled components, gotsrpc

Backend guides

Write fast and reliable services in Go and run them in the cloud

Projects

Foomo projects - libraries, utilities, friendly daemons and more

CMS

foomo has extensive support for headless CMS Systems

Architecture

Show me the big picture - how does foomo integrate into the k8s and Next.js eco system

Awesome Software

a curated list of software we use in the cloud, on our desktops and our devices

DevOps

our approach to run cloud native applications where they belong

Blog

random information about programming, our ecosystem and misc fun topics

- - +

foomo project docs

Stuff we wrote, because nobody else wanted to


foomo is an open source project, that has been maintained by the bestbytes team since 2011. It provides a wide range of utilities, libraries and daemons, that help us to tackle challenging projects.

foomo can help, if you are

  • using Go to write services
  • building frontends with TypeScript and Next.js
  • running your software on k8s
  • looking for a solution to deeply integrate your frontends with a headless cms
If more than two points apply, it will actually help a LOT

General guides

no matter if frontend or backend - there is somtehing for everyone in here

Frontend guides

Build frontends with TypeScript, Next.js, styled components, gotsrpc

Backend guides

Write fast and reliable services in Go and run them in the cloud

Projects

Foomo projects - libraries, utilities, friendly daemons and more

CMS

foomo has extensive support for headless CMS Systems

Architecture

Show me the big picture - how does foomo integrate into the k8s and Next.js eco system

Awesome Software

a curated list of software we use in the cloud, on our desktops and our devices

DevOps

our approach to run cloud native applications where they belong

Blog

random information about programming, our ecosystem and misc fun topics

+ + \ No newline at end of file diff --git a/search.html b/search.html index 6c63754..60c05c1 100644 --- a/search.html +++ b/search.html @@ -1,19 +1,21 @@ - + - - - + +Search the documentation | foomo project docs -Search the documentation | foomo project docs - - + + + + + + - +
-

Search the documentation

- - +

Search the documentation

+ + \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 941d9a3..9953447 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1 +1 @@ -https://www.foomo.org/awesome-softwareweekly0.5https://www.foomo.org/blogweekly0.5https://www.foomo.org/blog/archiveweekly0.5https://www.foomo.org/blog/debugging-go-map-races-in-k8sweekly0.5https://www.foomo.org/blog/impact-of-3rd-party-scripts-on-performanceweekly0.5https://www.foomo.org/blog/prometheus-cardinality-issuesweekly0.5https://www.foomo.org/blog/searching-for-search-enginesweekly0.5https://www.foomo.org/blog/tagsweekly0.5https://www.foomo.org/blog/tags/backendweekly0.5https://www.foomo.org/blog/tags/bundleweekly0.5https://www.foomo.org/blog/tags/bundle-sizeweekly0.5https://www.foomo.org/blog/tags/cardinalityweekly0.5https://www.foomo.org/blog/tags/debuggingweekly0.5https://www.foomo.org/blog/tags/devopsweekly0.5https://www.foomo.org/blog/tags/foomoweekly0.5https://www.foomo.org/blog/tags/frontendweekly0.5https://www.foomo.org/blog/tags/goweekly0.5https://www.foomo.org/blog/tags/javascriptweekly0.5https://www.foomo.org/blog/tags/k-8-sweekly0.5https://www.foomo.org/blog/tags/memoryweekly0.5https://www.foomo.org/blog/tags/oomweekly0.5https://www.foomo.org/blog/tags/opsweekly0.5https://www.foomo.org/blog/tags/performanceweekly0.5https://www.foomo.org/blog/tags/prometheusweekly0.5https://www.foomo.org/blog/tags/searchweekly0.5https://www.foomo.org/blog/tags/search-engineweekly0.5https://www.foomo.org/blog/welcome-back-2021weekly0.5https://www.foomo.org/blog/why-bundle-size-is-importantweekly0.5https://www.foomo.org/docs/tagsweekly0.5https://www.foomo.org/docs/tags/frontendweekly0.5https://www.foomo.org/docs/tags/overviewweekly0.5https://www.foomo.org/etc/imprintweekly0.5https://www.foomo.org/searchweekly0.5https://www.foomo.org/docs/backend/go-by-example/map-racingweekly0.5https://www.foomo.org/docs/backend/go-by-example/nil-mapsweekly0.5https://www.foomo.org/docs/backend/go-by-example/panic-and-recoverweekly0.5https://www.foomo.org/docs/backend/go-by-example/rangingweekly0.5https://www.foomo.org/docs/backend/go-by-example/yaml-magicweekly0.5https://www.foomo.org/docs/backend/gograppleweekly0.5https://www.foomo.org/docs/backend/introweekly0.5https://www.foomo.org/docs/backend/rtfmweekly0.5https://www.foomo.org/docs/backend/setupweekly0.5https://www.foomo.org/docs/devops/bookmarksweekly0.5https://www.foomo.org/docs/devops/introweekly0.5https://www.foomo.org/docs/devops/k8sweekly0.5https://www.foomo.org/docs/devops/kubernetes/spot-instance-node-poolsweekly0.5https://www.foomo.org/docs/devops/monitoring/grafanaweekly0.5https://www.foomo.org/docs/devops/monitoring/introweekly0.5https://www.foomo.org/docs/devops/monitoring/jaegerweekly0.5https://www.foomo.org/docs/frontend/cssweekly0.5https://www.foomo.org/docs/frontend/debugging_jsweekly0.5https://www.foomo.org/docs/frontend/introweekly0.5https://www.foomo.org/docs/frontend/performanceweekly0.5https://www.foomo.org/docs/frontend/pitfallsweekly0.5https://www.foomo.org/docs/frontend/rtfmweekly0.5https://www.foomo.org/docs/frontend/setupweekly0.5https://www.foomo.org/docs/frontend/stackweekly0.5https://www.foomo.org/docs/frontend/typescript/objectsweekly0.5https://www.foomo.org/docs/frontend/typescript/spreadingweekly0.5https://www.foomo.org/docs/frontend/vscodeweekly0.5https://www.foomo.org/docs/general/essentialsweekly0.5https://www.foomo.org/docs/general/introweekly0.5https://www.foomo.org/docs/general/package-managersweekly0.5https://www.foomo.org/docs/general/securityweekly0.5https://www.foomo.org/docs/general/setup/computerweekly0.5https://www.foomo.org/docs/general/setup/workplaceweekly0.5https://www.foomo.org/docs/general/utilities/k9sweekly0.5https://www.foomo.org/docs/general/work/generalweekly0.5https://www.foomo.org/docs/general/work/remote-workweekly0.5https://www.foomo.org/docs/project-management/introweekly0.5https://www.foomo.org/docs/project-management/responsibilityweekly0.5https://www.foomo.org/docs/projects/architectureweekly0.5https://www.foomo.org/docs/projects/cms/contentfulweekly0.5https://www.foomo.org/docs/projects/cms/contentserverweekly0.5https://www.foomo.org/docs/projects/cms/introweekly0.5https://www.foomo.org/docs/projects/gotsrpcweekly0.5https://www.foomo.org/docs/projects/introweekly0.5https://www.foomo.org/docs/projects/libraries/keelweekly0.5https://www.foomo.org/docs/projects/pagespeed-exporterweekly0.5https://www.foomo.org/docs/projects/site-reliabilityweekly0.5https://www.foomo.org/docs/projects/utilities/gograppleweekly0.5https://www.foomo.org/docs/projects/webgrappleweekly0.5https://www.foomo.org/weekly0.5 \ No newline at end of file +https://www.foomo.org/awesome-softwareweekly0.5https://www.foomo.org/blogweekly0.5https://www.foomo.org/blog/archiveweekly0.5https://www.foomo.org/blog/debugging-go-map-races-in-k8sweekly0.5https://www.foomo.org/blog/impact-of-3rd-party-scripts-on-performanceweekly0.5https://www.foomo.org/blog/prometheus-cardinality-issuesweekly0.5https://www.foomo.org/blog/searching-for-search-enginesweekly0.5https://www.foomo.org/blog/tagsweekly0.5https://www.foomo.org/blog/tags/backendweekly0.5https://www.foomo.org/blog/tags/bundleweekly0.5https://www.foomo.org/blog/tags/bundle-sizeweekly0.5https://www.foomo.org/blog/tags/cardinalityweekly0.5https://www.foomo.org/blog/tags/debuggingweekly0.5https://www.foomo.org/blog/tags/devopsweekly0.5https://www.foomo.org/blog/tags/foomoweekly0.5https://www.foomo.org/blog/tags/frontendweekly0.5https://www.foomo.org/blog/tags/goweekly0.5https://www.foomo.org/blog/tags/javascriptweekly0.5https://www.foomo.org/blog/tags/k-8-sweekly0.5https://www.foomo.org/blog/tags/memoryweekly0.5https://www.foomo.org/blog/tags/oomweekly0.5https://www.foomo.org/blog/tags/opsweekly0.5https://www.foomo.org/blog/tags/performanceweekly0.5https://www.foomo.org/blog/tags/prometheusweekly0.5https://www.foomo.org/blog/tags/searchweekly0.5https://www.foomo.org/blog/tags/search-engineweekly0.5https://www.foomo.org/blog/welcome-back-2021weekly0.5https://www.foomo.org/blog/why-bundle-size-is-importantweekly0.5https://www.foomo.org/docs/tagsweekly0.5https://www.foomo.org/docs/tags/frontendweekly0.5https://www.foomo.org/docs/tags/goweekly0.5https://www.foomo.org/docs/tags/javascriptweekly0.5https://www.foomo.org/docs/tags/overviewweekly0.5https://www.foomo.org/docs/tags/sseweekly0.5https://www.foomo.org/etc/imprintweekly0.5https://www.foomo.org/searchweekly0.5https://www.foomo.org/docs/backend/go-by-example/map-racingweekly0.5https://www.foomo.org/docs/backend/go-by-example/nil-mapsweekly0.5https://www.foomo.org/docs/backend/go-by-example/panic-and-recoverweekly0.5https://www.foomo.org/docs/backend/go-by-example/rangingweekly0.5https://www.foomo.org/docs/backend/go-by-example/yaml-magicweekly0.5https://www.foomo.org/docs/backend/gograppleweekly0.5https://www.foomo.org/docs/backend/introweekly0.5https://www.foomo.org/docs/backend/rtfmweekly0.5https://www.foomo.org/docs/backend/setupweekly0.5https://www.foomo.org/docs/devops/bookmarksweekly0.5https://www.foomo.org/docs/devops/introweekly0.5https://www.foomo.org/docs/devops/k8sweekly0.5https://www.foomo.org/docs/devops/kubernetes/spot-instance-node-poolsweekly0.5https://www.foomo.org/docs/devops/monitoring/grafanaweekly0.5https://www.foomo.org/docs/devops/monitoring/introweekly0.5https://www.foomo.org/docs/devops/monitoring/jaegerweekly0.5https://www.foomo.org/docs/frontend/cssweekly0.5https://www.foomo.org/docs/frontend/debugging_jsweekly0.5https://www.foomo.org/docs/frontend/introweekly0.5https://www.foomo.org/docs/frontend/performanceweekly0.5https://www.foomo.org/docs/frontend/pitfallsweekly0.5https://www.foomo.org/docs/frontend/rtfmweekly0.5https://www.foomo.org/docs/frontend/setupweekly0.5https://www.foomo.org/docs/frontend/stackweekly0.5https://www.foomo.org/docs/frontend/typescript/objectsweekly0.5https://www.foomo.org/docs/frontend/typescript/spreadingweekly0.5https://www.foomo.org/docs/frontend/vscodeweekly0.5https://www.foomo.org/docs/general/essentialsweekly0.5https://www.foomo.org/docs/general/introweekly0.5https://www.foomo.org/docs/general/package-managersweekly0.5https://www.foomo.org/docs/general/securityweekly0.5https://www.foomo.org/docs/general/setup/computerweekly0.5https://www.foomo.org/docs/general/setup/workplaceweekly0.5https://www.foomo.org/docs/general/technologies/sseweekly0.5https://www.foomo.org/docs/general/utilities/k9sweekly0.5https://www.foomo.org/docs/general/work/generalweekly0.5https://www.foomo.org/docs/general/work/remote-workweekly0.5https://www.foomo.org/docs/project-management/introweekly0.5https://www.foomo.org/docs/project-management/responsibilityweekly0.5https://www.foomo.org/docs/projects/architectureweekly0.5https://www.foomo.org/docs/projects/cms/contentfulweekly0.5https://www.foomo.org/docs/projects/cms/contentserverweekly0.5https://www.foomo.org/docs/projects/cms/introweekly0.5https://www.foomo.org/docs/projects/gotsrpcweekly0.5https://www.foomo.org/docs/projects/introweekly0.5https://www.foomo.org/docs/projects/libraries/keelweekly0.5https://www.foomo.org/docs/projects/pagespeed-exporterweekly0.5https://www.foomo.org/docs/projects/site-reliabilityweekly0.5https://www.foomo.org/docs/projects/utilities/gograppleweekly0.5https://www.foomo.org/docs/projects/webgrappleweekly0.5https://www.foomo.org/weekly0.5 \ No newline at end of file