diff --git a/404.html b/404.html index 5a39bbcf6..224cfeab8 100644 --- a/404.html +++ b/404.html @@ -16,8 +16,8 @@ - - + +
diff --git a/assets/js/23f588f5.a08eb685.js b/assets/js/23f588f5.a08eb685.js deleted file mode 100644 index 6749fdb31..000000000 --- a/assets/js/23f588f5.a08eb685.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[7434],{4299:(e,s,t)=>{t.r(s),t.d(s,{assets:()=>o,contentTitle:()=>a,default:()=>h,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var n=t(4848),r=t(8453);const i={title:"Releasing 0.2.0",description:"Making workflow development easy again.",slug:"littlehorse-0.2.0-release",authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],tags:["littlehorse","release"],image:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",hide_table_of_contents:!1},a=void 0,l={permalink:"/blog/littlehorse-0.2.0-release",source:"@site/blog/2023-08-30-0.2.0-release.md",title:"Releasing 0.2.0",description:"Making workflow development easy again.",date:"2023-08-30T00:00:00.000Z",tags:[{label:"littlehorse",permalink:"/blog/tags/littlehorse"},{label:"release",permalink:"/blog/tags/release"}],readingTime:3.54,hasTruncateMarker:!0,authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",imageURL:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],frontMatter:{title:"Releasing 0.2.0",description:"Making workflow development easy again.",slug:"littlehorse-0.2.0-release",authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",imageURL:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],tags:["littlehorse","release"],image:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",hide_table_of_contents:!1},unlisted:!1,prevItem:{title:"Helm and Kubernetes Operators",permalink:"/blog/helm-and-k8s-operators"}},o={authorsImageUrls:[void 0]},c=[{value:"Get Started",id:"get-started",level:2},{value:"New Features",id:"new-features",level:2},{value:"User Tasks",id:"user-tasks",level:3},{value:"Workflow Threading",id:"workflow-threading",level:3},{value:"Python Support",id:"python-support",level:3},{value:"Security",id:"security",level:3},{value:"Performance",id:"performance",level:3},{value:"Go Support",id:"go-support",level:3},{value:"What's Next",id:"whats-next",level:2}];function d(e){const s={a:"a",admonition:"admonition",code:"code",em:"em",h2:"h2",h3:"h3",li:"li",p:"p",ul:"ul",...(0,r.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)(s.p,{children:["We are excited to announce the release of ",(0,n.jsx)(s.code,{children:"0.2.0"}),"! In this release, we added several new features, highlighted by User Tasks, security, and Python support."]}),"\n",(0,n.jsx)(s.h2,{id:"get-started",children:"Get Started"}),"\n",(0,n.jsx)(s.p,{children:"LittleHorse is free for production use according to the Server-Side Public License!"}),"\n",(0,n.jsx)(s.p,{children:"To get started with LittleHorse OSS, you can:"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:["Visit us on ",(0,n.jsx)(s.a,{href:"https://github.com/littlehorse-enterprises",children:"GitHub"})]}),"\n",(0,n.jsxs)(s.li,{children:["Try our ",(0,n.jsx)(s.a,{href:"https://littlehorse.dev/docs/developer-guide/install#installation-and-quickstart",children:"quickstarts"})]}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["Additionally, with version ",(0,n.jsx)(s.code,{children:"0.2.0"}),", we have released our first two Docker Images:"]}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.a,{href:"https://gallery.ecr.aws/littlehorse/littlehorse-server",children:(0,n.jsx)(s.code,{children:"lh-server"})}),", the production-ready build of the LittleHorse Server."]}),"\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.a,{href:"https://gallery.ecr.aws/littlehorse/littlehorse-standalone",children:(0,n.jsx)(s.code,{children:"lh-standalone"})}),", a self-contained build of the LittleHorse Server that you can run to get a working LH Installation for local development."]}),"\n"]}),"\n",(0,n.jsx)(s.h2,{id:"new-features",children:"New Features"}),"\n",(0,n.jsxs)(s.p,{children:["Release ",(0,n.jsx)(s.code,{children:"0.2.0"})," contains many exciting new features, and we've highlighted a few here."]}),"\n",(0,n.jsx)(s.h3,{id:"user-tasks",children:"User Tasks"}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.a,{href:"https://littlehorse.dev/docs/concepts/user-tasks",children:"User Tasks"})," are a massive new feature released in ",(0,n.jsx)(s.code,{children:"0.2.0"})," which allow you to schedule tasks to be executed by a human user alongside tasks that are executed by computers."]}),"\n",(0,n.jsxs)(s.p,{children:["In ",(0,n.jsx)(s.code,{children:"0.2.0"}),", User Tasks have reached stability, meaning that future releases will be backwards-compatible with the current User Tasks API. We currently have the following features:"]}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsx)(s.li,{children:"Assignment of tasks to a User or User Group"}),"\n",(0,n.jsxs)(s.li,{children:["Reminder Tasks, or ",(0,n.jsx)(s.code,{children:"TaskRun"}),"'s that are scheduled some time after a ",(0,n.jsx)(s.code,{children:"UserTaskRun"})," is scheduled."]}),"\n",(0,n.jsxs)(s.li,{children:["Automatic reassignment of a ",(0,n.jsx)(s.code,{children:"UserTaskRun"})," after some period of inactivity."]}),"\n",(0,n.jsxs)(s.li,{children:["Manual reassignment of a ",(0,n.jsx)(s.code,{children:"UserTaskRun"}),"."]}),"\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.code,{children:"UserTaskRun"})," search."]}),"\n"]}),"\n",(0,n.jsxs)(s.admonition,{type:"note",children:[(0,n.jsxs)(s.p,{children:["The public API for User Tasks is stable in all of the grpc clients and in the Java ",(0,n.jsx)(s.code,{children:"WfSpec"})," SDK."]}),(0,n.jsxs)(s.p,{children:["The Go and Python grpc clients both support User Tasks. However, neither Python nor Go yet have support for User Tasks in the ",(0,n.jsx)(s.code,{children:"WfSpec"})," SDK."]})]}),"\n",(0,n.jsx)(s.h3,{id:"workflow-threading",children:"Workflow Threading"}),"\n",(0,n.jsxs)(s.p,{children:["Release ",(0,n.jsx)(s.code,{children:"0.2.0"})," allows you to use a ",(0,n.jsx)(s.code,{children:"WAIT_FOR_THREADS"})," node to wait for more than one child thread at one time. For an example, see our ",(0,n.jsx)(s.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/tree/master/examples/parallel-approval",children:"Parallel Approval Example"})," on our GitHub."]}),"\n",(0,n.jsxs)(s.p,{children:["Future releases will provide ",(0,n.jsx)(s.em,{children:"backwards-compatible"})," enhancements to this\nfunctionality, allowing various strategies for handling failures of individual child threads."]}),"\n",(0,n.jsx)(s.h3,{id:"python-support",children:"Python Support"}),"\n",(0,n.jsxs)(s.p,{children:["We have released an alpha ",(0,n.jsx)(s.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/tree/master/sdk-python",children:"Python SDK"}),"! This release contains:"]}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsx)(s.li,{children:"Python client in grpc"}),"\n",(0,n.jsx)(s.li,{children:"Python Task Worker SDK"}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["Currently, building ",(0,n.jsx)(s.code,{children:"WfSpec"}),"'s in Python is not supported. We aim to move python Task Worker support from alpha to beta, and add alpha support for ",(0,n.jsx)(s.code,{children:"WfSpec"})," development in python, in the ",(0,n.jsx)(s.code,{children:"0.3.0"})," release."]}),"\n",(0,n.jsxs)(s.p,{children:["To try out our python task worker client, you can head to ",(0,n.jsx)(s.a,{href:"https://littlehorse.dev/docs/developer-guide/install",children:"Installation Docs"})," and the ",(0,n.jsx)(s.a,{href:"https://littlehorse.dev/docs/developer-guide/task-worker-development",children:"Task Worker Development Docs"}),"."]}),"\n",(0,n.jsx)(s.admonition,{type:"note",children:(0,n.jsx)(s.p,{children:"The Python SDK is in the alpha stage, meaning that future releases could break backwards compatibility."})}),"\n",(0,n.jsx)(s.h3,{id:"security",children:"Security"}),"\n",(0,n.jsxs)(s.p,{children:["We added beta support for OAuth, TLS, and mTLS in release ",(0,n.jsx)(s.code,{children:"0.2.0"}),'. The following features graduated to "beta" in this release:']}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsx)(s.li,{children:"TLS encryption for incoming connections on all listeners, configured on a per-listener basis."}),"\n",(0,n.jsx)(s.li,{children:"mTLS to authenticate incoming connections on any listeners, configured on a per-listener basis."}),"\n",(0,n.jsx)(s.li,{children:"OAuth to authenticate incoming connections on any public listener (excluding the inter-server communication port)."}),"\n"]}),"\n",(0,n.jsxs)(s.admonition,{type:"info",children:[(0,n.jsxs)(s.p,{children:["Beta support means that we will soon add significant functionality, and as such a future release ",(0,n.jsx)(s.em,{children:"might"})," break backwards compatibility."]}),(0,n.jsxs)(s.p,{children:["However, future releases of a feature in the ",(0,n.jsx)(s.em,{children:"beta"})," state will most likely be backwards compatible with ",(0,n.jsx)(s.code,{children:"0.2.0"})," barring exceptional circumstances."]})]}),"\n",(0,n.jsx)(s.h3,{id:"performance",children:"Performance"}),"\n",(0,n.jsxs)(s.p,{children:["We made several optimizations to our storage management sub-system, reducing the number of put's and get's into our backing state store by roughly 30%. As a result, a LittleHorse Server running with a single partition is capable of scheduling over 1,100 ",(0,n.jsx)(s.code,{children:"TaskRun"}),"'s per second."]}),"\n",(0,n.jsx)(s.h3,{id:"go-support",children:"Go Support"}),"\n",(0,n.jsx)(s.p,{children:"Support for the Go client is now beta. Future releases will maintain compatibility for all features on our documentation."}),"\n",(0,n.jsxs)(s.p,{children:["Release ",(0,n.jsx)(s.code,{children:"0.3.0"})," will close the gap between the Java and Go SDK's, adding features such as:"]}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:["Format Strings for Variable Assignments in the ",(0,n.jsx)(s.code,{children:"WfSpec"})," SDK"]}),"\n",(0,n.jsxs)(s.li,{children:["User Task support in the ",(0,n.jsx)(s.code,{children:"WfSpec"})," SDK"]}),"\n",(0,n.jsxs)(s.li,{children:["Configuring Indexes on ",(0,n.jsx)(s.code,{children:"Variable"}),"s in the ",(0,n.jsx)(s.code,{children:"WfSpec"})," SDK"]}),"\n"]}),"\n",(0,n.jsx)(s.h2,{id:"whats-next",children:"What's Next"}),"\n",(0,n.jsx)(s.p,{children:"We have several exciting features coming soon over the next few releases, including:"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsx)(s.li,{children:"Fine-grained access controls"}),"\n",(0,n.jsxs)(s.li,{children:["Backward-compatible improvements to ",(0,n.jsx)(s.a,{href:"https://littlehorse.dev/docs/concepts/exception-handling",children:"Failure Handling"})]}),"\n",(0,n.jsx)(s.li,{children:"C# support"}),"\n",(0,n.jsxs)(s.li,{children:["Python support for building ",(0,n.jsx)(s.code,{children:"WfSpec"}),"s"]}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["For an enterprise-ready distribution of LittleHorse running in your own datacenter, contact ",(0,n.jsx)(s.code,{children:"sales@littlehorse.io"})," to inquire about LittleHorse Platform."]}),"\n",(0,n.jsxs)(s.p,{children:["For a pay-as-you-go, serverless Managed Service of LittleHorse in the cloud, fill out the ",(0,n.jsx)(s.a,{href:"https://docs.google.com/forms/d/e/1FAIpQLScXVvTYy4LQnYoFoRKRQ7ppuxe0KgncsDukvm96qKN0pU5TnQ/viewform",children:"LH Cloud Waitlist Form"}),"."]})]})}function h(e={}){const{wrapper:s}={...(0,r.R)(),...e.components};return s?(0,n.jsx)(s,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453:(e,s,t)=>{t.d(s,{R:()=>a,x:()=>l});var n=t(6540);const r={},i=n.createContext(r);function a(e){const s=n.useContext(i);return n.useMemo((function(){return"function"==typeof e?e(s):{...s,...e}}),[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:a(e.components),n.createElement(i.Provider,{value:s},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/23f588f5.d644a20d.js b/assets/js/23f588f5.d644a20d.js new file mode 100644 index 000000000..7c03e21a5 --- /dev/null +++ b/assets/js/23f588f5.d644a20d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[7434],{4299:(e,s,t)=>{t.r(s),t.d(s,{assets:()=>o,contentTitle:()=>a,default:()=>h,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var n=t(4848),r=t(8453);const i={title:"Releasing 0.2.0",description:"Making workflow development easy again.",slug:"littlehorse-0.2.0-release",authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],tags:["littlehorse","release"],image:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",hide_table_of_contents:!1},a=void 0,l={permalink:"/blog/littlehorse-0.2.0-release",source:"@site/blog/2023-08-30-0.2.0-release.md",title:"Releasing 0.2.0",description:"Making workflow development easy again.",date:"2023-08-30T00:00:00.000Z",tags:[{label:"littlehorse",permalink:"/blog/tags/littlehorse"},{label:"release",permalink:"/blog/tags/release"}],readingTime:3.54,hasTruncateMarker:!0,authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",imageURL:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],frontMatter:{title:"Releasing 0.2.0",description:"Making workflow development easy again.",slug:"littlehorse-0.2.0-release",authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",imageURL:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],tags:["littlehorse","release"],image:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",hide_table_of_contents:!1},unlisted:!1,prevItem:{title:"Helm and Kubernetes Operators",permalink:"/blog/helm-and-k8s-operators"},nextItem:{title:"Releasing 0.8",permalink:"/blog/littlehorse-0.8-release"}},o={authorsImageUrls:[void 0]},c=[{value:"Get Started",id:"get-started",level:2},{value:"New Features",id:"new-features",level:2},{value:"User Tasks",id:"user-tasks",level:3},{value:"Workflow Threading",id:"workflow-threading",level:3},{value:"Python Support",id:"python-support",level:3},{value:"Security",id:"security",level:3},{value:"Performance",id:"performance",level:3},{value:"Go Support",id:"go-support",level:3},{value:"What's Next",id:"whats-next",level:2}];function d(e){const s={a:"a",admonition:"admonition",code:"code",em:"em",h2:"h2",h3:"h3",li:"li",p:"p",ul:"ul",...(0,r.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsxs)(s.p,{children:["We are excited to announce the release of ",(0,n.jsx)(s.code,{children:"0.2.0"}),"! In this release, we added several new features, highlighted by User Tasks, security, and Python support."]}),"\n",(0,n.jsx)(s.h2,{id:"get-started",children:"Get Started"}),"\n",(0,n.jsx)(s.p,{children:"LittleHorse is free for production use according to the Server-Side Public License!"}),"\n",(0,n.jsx)(s.p,{children:"To get started with LittleHorse OSS, you can:"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:["Visit us on ",(0,n.jsx)(s.a,{href:"https://github.com/littlehorse-enterprises",children:"GitHub"})]}),"\n",(0,n.jsxs)(s.li,{children:["Try our ",(0,n.jsx)(s.a,{href:"https://littlehorse.dev/docs/developer-guide/install#installation-and-quickstart",children:"quickstarts"})]}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["Additionally, with version ",(0,n.jsx)(s.code,{children:"0.2.0"}),", we have released our first two Docker Images:"]}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.a,{href:"https://gallery.ecr.aws/littlehorse/littlehorse-server",children:(0,n.jsx)(s.code,{children:"lh-server"})}),", the production-ready build of the LittleHorse Server."]}),"\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.a,{href:"https://gallery.ecr.aws/littlehorse/littlehorse-standalone",children:(0,n.jsx)(s.code,{children:"lh-standalone"})}),", a self-contained build of the LittleHorse Server that you can run to get a working LH Installation for local development."]}),"\n"]}),"\n",(0,n.jsx)(s.h2,{id:"new-features",children:"New Features"}),"\n",(0,n.jsxs)(s.p,{children:["Release ",(0,n.jsx)(s.code,{children:"0.2.0"})," contains many exciting new features, and we've highlighted a few here."]}),"\n",(0,n.jsx)(s.h3,{id:"user-tasks",children:"User Tasks"}),"\n",(0,n.jsxs)(s.p,{children:[(0,n.jsx)(s.a,{href:"https://littlehorse.dev/docs/concepts/user-tasks",children:"User Tasks"})," are a massive new feature released in ",(0,n.jsx)(s.code,{children:"0.2.0"})," which allow you to schedule tasks to be executed by a human user alongside tasks that are executed by computers."]}),"\n",(0,n.jsxs)(s.p,{children:["In ",(0,n.jsx)(s.code,{children:"0.2.0"}),", User Tasks have reached stability, meaning that future releases will be backwards-compatible with the current User Tasks API. We currently have the following features:"]}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsx)(s.li,{children:"Assignment of tasks to a User or User Group"}),"\n",(0,n.jsxs)(s.li,{children:["Reminder Tasks, or ",(0,n.jsx)(s.code,{children:"TaskRun"}),"'s that are scheduled some time after a ",(0,n.jsx)(s.code,{children:"UserTaskRun"})," is scheduled."]}),"\n",(0,n.jsxs)(s.li,{children:["Automatic reassignment of a ",(0,n.jsx)(s.code,{children:"UserTaskRun"})," after some period of inactivity."]}),"\n",(0,n.jsxs)(s.li,{children:["Manual reassignment of a ",(0,n.jsx)(s.code,{children:"UserTaskRun"}),"."]}),"\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.code,{children:"UserTaskRun"})," search."]}),"\n"]}),"\n",(0,n.jsxs)(s.admonition,{type:"note",children:[(0,n.jsxs)(s.p,{children:["The public API for User Tasks is stable in all of the grpc clients and in the Java ",(0,n.jsx)(s.code,{children:"WfSpec"})," SDK."]}),(0,n.jsxs)(s.p,{children:["The Go and Python grpc clients both support User Tasks. However, neither Python nor Go yet have support for User Tasks in the ",(0,n.jsx)(s.code,{children:"WfSpec"})," SDK."]})]}),"\n",(0,n.jsx)(s.h3,{id:"workflow-threading",children:"Workflow Threading"}),"\n",(0,n.jsxs)(s.p,{children:["Release ",(0,n.jsx)(s.code,{children:"0.2.0"})," allows you to use a ",(0,n.jsx)(s.code,{children:"WAIT_FOR_THREADS"})," node to wait for more than one child thread at one time. For an example, see our ",(0,n.jsx)(s.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/tree/master/examples/parallel-approval",children:"Parallel Approval Example"})," on our GitHub."]}),"\n",(0,n.jsxs)(s.p,{children:["Future releases will provide ",(0,n.jsx)(s.em,{children:"backwards-compatible"})," enhancements to this\nfunctionality, allowing various strategies for handling failures of individual child threads."]}),"\n",(0,n.jsx)(s.h3,{id:"python-support",children:"Python Support"}),"\n",(0,n.jsxs)(s.p,{children:["We have released an alpha ",(0,n.jsx)(s.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/tree/master/sdk-python",children:"Python SDK"}),"! This release contains:"]}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsx)(s.li,{children:"Python client in grpc"}),"\n",(0,n.jsx)(s.li,{children:"Python Task Worker SDK"}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["Currently, building ",(0,n.jsx)(s.code,{children:"WfSpec"}),"'s in Python is not supported. We aim to move python Task Worker support from alpha to beta, and add alpha support for ",(0,n.jsx)(s.code,{children:"WfSpec"})," development in python, in the ",(0,n.jsx)(s.code,{children:"0.3.0"})," release."]}),"\n",(0,n.jsxs)(s.p,{children:["To try out our python task worker client, you can head to ",(0,n.jsx)(s.a,{href:"https://littlehorse.dev/docs/developer-guide/install",children:"Installation Docs"})," and the ",(0,n.jsx)(s.a,{href:"https://littlehorse.dev/docs/developer-guide/task-worker-development",children:"Task Worker Development Docs"}),"."]}),"\n",(0,n.jsx)(s.admonition,{type:"note",children:(0,n.jsx)(s.p,{children:"The Python SDK is in the alpha stage, meaning that future releases could break backwards compatibility."})}),"\n",(0,n.jsx)(s.h3,{id:"security",children:"Security"}),"\n",(0,n.jsxs)(s.p,{children:["We added beta support for OAuth, TLS, and mTLS in release ",(0,n.jsx)(s.code,{children:"0.2.0"}),'. The following features graduated to "beta" in this release:']}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsx)(s.li,{children:"TLS encryption for incoming connections on all listeners, configured on a per-listener basis."}),"\n",(0,n.jsx)(s.li,{children:"mTLS to authenticate incoming connections on any listeners, configured on a per-listener basis."}),"\n",(0,n.jsx)(s.li,{children:"OAuth to authenticate incoming connections on any public listener (excluding the inter-server communication port)."}),"\n"]}),"\n",(0,n.jsxs)(s.admonition,{type:"info",children:[(0,n.jsxs)(s.p,{children:["Beta support means that we will soon add significant functionality, and as such a future release ",(0,n.jsx)(s.em,{children:"might"})," break backwards compatibility."]}),(0,n.jsxs)(s.p,{children:["However, future releases of a feature in the ",(0,n.jsx)(s.em,{children:"beta"})," state will most likely be backwards compatible with ",(0,n.jsx)(s.code,{children:"0.2.0"})," barring exceptional circumstances."]})]}),"\n",(0,n.jsx)(s.h3,{id:"performance",children:"Performance"}),"\n",(0,n.jsxs)(s.p,{children:["We made several optimizations to our storage management sub-system, reducing the number of put's and get's into our backing state store by roughly 30%. As a result, a LittleHorse Server running with a single partition is capable of scheduling over 1,100 ",(0,n.jsx)(s.code,{children:"TaskRun"}),"'s per second."]}),"\n",(0,n.jsx)(s.h3,{id:"go-support",children:"Go Support"}),"\n",(0,n.jsx)(s.p,{children:"Support for the Go client is now beta. Future releases will maintain compatibility for all features on our documentation."}),"\n",(0,n.jsxs)(s.p,{children:["Release ",(0,n.jsx)(s.code,{children:"0.3.0"})," will close the gap between the Java and Go SDK's, adding features such as:"]}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:["Format Strings for Variable Assignments in the ",(0,n.jsx)(s.code,{children:"WfSpec"})," SDK"]}),"\n",(0,n.jsxs)(s.li,{children:["User Task support in the ",(0,n.jsx)(s.code,{children:"WfSpec"})," SDK"]}),"\n",(0,n.jsxs)(s.li,{children:["Configuring Indexes on ",(0,n.jsx)(s.code,{children:"Variable"}),"s in the ",(0,n.jsx)(s.code,{children:"WfSpec"})," SDK"]}),"\n"]}),"\n",(0,n.jsx)(s.h2,{id:"whats-next",children:"What's Next"}),"\n",(0,n.jsx)(s.p,{children:"We have several exciting features coming soon over the next few releases, including:"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsx)(s.li,{children:"Fine-grained access controls"}),"\n",(0,n.jsxs)(s.li,{children:["Backward-compatible improvements to ",(0,n.jsx)(s.a,{href:"https://littlehorse.dev/docs/concepts/exception-handling",children:"Failure Handling"})]}),"\n",(0,n.jsx)(s.li,{children:"C# support"}),"\n",(0,n.jsxs)(s.li,{children:["Python support for building ",(0,n.jsx)(s.code,{children:"WfSpec"}),"s"]}),"\n"]}),"\n",(0,n.jsxs)(s.p,{children:["For an enterprise-ready distribution of LittleHorse running in your own datacenter, contact ",(0,n.jsx)(s.code,{children:"sales@littlehorse.io"})," to inquire about LittleHorse Platform."]}),"\n",(0,n.jsxs)(s.p,{children:["For a pay-as-you-go, serverless Managed Service of LittleHorse in the cloud, fill out the ",(0,n.jsx)(s.a,{href:"https://docs.google.com/forms/d/e/1FAIpQLScXVvTYy4LQnYoFoRKRQ7ppuxe0KgncsDukvm96qKN0pU5TnQ/viewform",children:"LH Cloud Waitlist Form"}),"."]})]})}function h(e={}){const{wrapper:s}={...(0,r.R)(),...e.components};return s?(0,n.jsx)(s,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453:(e,s,t)=>{t.d(s,{R:()=>a,x:()=>l});var n=t(6540);const r={},i=n.createContext(r);function a(e){const s=n.useContext(i);return n.useMemo((function(){return"function"==typeof e?e(s):{...s,...e}}),[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:a(e.components),n.createElement(i.Provider,{value:s},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/25003e4f.e8d998ea.js b/assets/js/25003e4f.0d2b0c1d.js similarity index 74% rename from assets/js/25003e4f.e8d998ea.js rename to assets/js/25003e4f.0d2b0c1d.js index da5101776..577cd87d4 100644 --- a/assets/js/25003e4f.e8d998ea.js +++ b/assets/js/25003e4f.0d2b0c1d.js @@ -1 +1 @@ -"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[2838],{4351:e=>{e.exports=JSON.parse('{"archive":{"blogPosts":[{"id":"promise-of-microservices","metadata":{"permalink":"/blog/promise-of-microservices","source":"@site/blog/2024-08-22-promise-of-microservices.md","title":"The Promise of Microservices","description":"\x3c!-- ---","date":"2024-08-22T00:00:00.000Z","tags":[{"label":"tech-trends","permalink":"/blog/tags/tech-trends"},{"label":"microservices","permalink":"/blog/tags/microservices"},{"label":"littlehorse","permalink":"/blog/tags/littlehorse"}],"readingTime":7.29,"hasTruncateMarker":true,"authors":[{"name":"Colt McNealy","title":"Managing Member of the LLC","url":"https://www.linkedin.com/in/colt-mcnealy-900b7a148/","image_url":"https://avatars.githubusercontent.com/u/100447728","imageURL":"https://avatars.githubusercontent.com/u/100447728"}],"frontMatter":{"slug":"promise-of-microservices","title":"The Promise of Microservices","authors":{"name":"Colt McNealy","title":"Managing Member of the LLC","url":"https://www.linkedin.com/in/colt-mcnealy-900b7a148/","image_url":"https://avatars.githubusercontent.com/u/100447728","imageURL":"https://avatars.githubusercontent.com/u/100447728"},"tags":["tech-trends","microservices","littlehorse"]},"unlisted":false,"nextItem":{"title":"Releasing 0.7","permalink":"/blog/littlehorse-0.7-release"}},"content":"\x3c!-- ---\\nBenefits of Microservices\\n- Different dev teams can work independently in parallel\\n- Bounded Contexts can theoretically ensure that smaller teams\\n- Faster deployment and iteration\\n- Take advantage of cloud native systems --\x3e\\n\\nIf microservices add so much complexity, why bother with the hassle? \x3c!-- truncate --\x3e\\n\\nWe\'ve all _heard of_ microservices, but unless you\'ve read copious amounts of Sam Newman and Adam Bellemare\'s writings, you might be wondering whether, when, and why you should adopt them. In this blog post, we will examine the halcyon land promised by microservices.\\n\\n:::info\\nThis is the first part of a 3-part blog series:\\n\\n1. **[This Post]** The Promise of Microservices\\n2. **[Coming Soon]** The Challenge with Microservices\\n3. **[Coming Soon]** Workflow and Microservices: A Match Made in Heaven\\n:::\\n\\nMicroservices have been [deployed widely](https://www.simform.com/blog/microservices-examples/) across many large enterprises, most notably Netflix, Uber, Shopify, PayPal, and others. As we will discover throughout this blog series, a microservice architecture is mandatory once you reach a certain size of company, and it\'s probably overkill for a 12-person startup. The gray area inbetween is the interesting part!\\n\\n## What are Microservices?\\n\\nThe term \\"microservices\\" refers to a software architecture wherein an enterprise application comprises a collection of small, loosely coupled, and independently deployable services (these small services are called \\"microservices\\" in contrast to larger monoliths). Each microservice focuses on a specific business capability and communicates with other services over a network, typically through API\'s, streaming platforms, or message queues.\\n\\nIn practice, this means that a user interaction with an application (such as placing an order) might trigger actions that occur in _many_ small, independently-deployed software systems, such as:\\n\\n* A Notification service\\n* An Inventory Management service\\n* A Payments service\\n* An Order History service\\n\\nFrom the user (client) perspective, one request is made (generally through a Load Balancer, API Gateway, or Ingress Controller) but that request may ping-pong between multiple back-end services and may also result in future actions being scheduled asynchronously:\\n\\n![Microservices Architecture](./2024-08-22-microservices-arch.png)\\n\\nIn contrast to microservices, a _monolithic_ architecture would serve the entire \\"place order\\" request on a single deployable artifact:\\n\\n![Monolithic Architecture](./2024-08-22-monolith-arch.png)\\n\\nIn Domain Driven Design, accidental complexity refers to the unintentional complexity that you introduced to your architecture (deployments, service interactions, third-party dependencies, etc.). Rule #1 of maintaining software systems is to avoid introducing accidental complexity as much as possible.\\n\\nSimply by looking at the visuals above, microservices add a significant dose of accidental complexity to your architecture (more on this in next week\'s post!). Given that, what benefits would make up for the extra complexity introduced by microservices?\\n\\n## Why Now?\\n\\nI would be first to admit that microservices bring with them a series of headaches around cost, observability, maintenance, and ease of evolution (otherwise, I would not have founded LittleHorse Enterprises!). However, microservice architecture plays a vital role in addressing two critical trends reshaping the software development landscape today:\\n\\n* Increased digitization of companies in all business sectors (accelerated by the rise of AI).\\n* Elasticity of cloud computing.\\n\\n### Increased Digitization\\n\\nThe level of digitization expected of businesses in order to compete in the modern market has drastically increased: IT teams must build software that interfaces with an ever-expanding list of external API\'s, legacy systems, user interfaces, internal tools, and SaaS providers.\\n\\nFor example: in the early 2000\'s, it was perfectly acceptable (even _expected_) for a passenger to book airline tickets over the telephone or through a travel agency. However, such an experience would be unheard of today and would immediately hobble an airline who provided such poor digital services.\\n\\nIn addition to using automation to provide better customer services, companies are generating, processing, and analyzing massive amounts of data. For example, grocery stores with razor-thin margins analyze seasonal consumption patterns in order to optimize inventory and prevent costly food waste.\\n\\nThese trends have coincided with (or _caused_, I would argue) a proliferation in the number of 1) software developers, and 2) software tools and API\'s found within companies in all industries, leading to two new problems:\\n\\n1. Allowing large teams of software developers to productively work on an enterprise application in parallel (without stepping on each others\' toes).\\n2. Ensuring that business requirements are effectively communicated to the entire (larger) software engineering team.\\n\\n### Cloud Elasticity\\n\\nAs the importance and quantity of digital software systems exploded over the last two decades, so has the availability of nearly-infinite compute power delivered through cloud infrastructure providers such as AWS.\\n\\nThe promise of _elasticity_, or the ability to quickly spin compute resources up or down according to load and only pay for what you use, is unique to the cloud: for on-prem datacenters, spinning up new compute means buying new machines from Sun Microsystems (hopefully not Microsoft!), and scaling down compute means trying to sell them off on the secondary market. (Ask my father about how that went for a lot of people in 2001.)\\n\\nBeyond scaling up and down, elasticity enables different deployment patterns that did not exist before. Whereas pre-cloud enterprises had dedicated and centralized data-center teams who were in charge of running applications, the accessibility of cloud computing gave rise to the DevOps movement. This has empowered smaller teams of software developers to take on the task of transferring software from \\"it works on my laptop!\\" to \\"it\'s now deployed in production!\\"\\n\\n## Why Microservices?\\n\\nDespite the extra complexity it brings, the microservice architecture can more than pay for itself by ensuring organizational alignment and allowing enterprise architectures to take full advantage of the cloud\'s elasticity.\\n\\n### Organizational Alignment\\n\\nAs discussed earlier, the business problems that software engineering organizations must solve today dwarf those that were solved in the 1990\'s, and so do the software engineering teams that tackle those problems.\\n\\n:::note\\nI am not belittling the engineers of the 90\'s; the problems they solved were arguably _much harder_ than the problems we face today, and there were fewer engineers to face those problems. However, it is a fact that users expect more digital-native experiences today than they did twenty years ago.\\n:::\\n\\nBy breaking applications into smaller services, we can accomplish several important things:\\n* Break up our software engineering team into smaller teams which are each responsible for individual microservices.\\n* Allow different components of a system to be developed with separate tech stacks and released independently.\\n\\nEngineering teams of over a few dozen engineers working on the same deployable piece of software is a recipe for inefficiency. Merge conflicts, arguments over tech stack, slow \\"release trains,\\" and excessive intra-team coordination are just a few problems that arise. However, by breaking your application into smaller microservices, you can also break up your engineering organization into smaller, more efficient teams each in charge of a small number (prefably one!) of microservices.\\n\\nAs an added benefit, properly-designed microservice architectures can follow the principles of Domain Driven Design. Ideally, a single microservice corresponds to a _Bounded Context_ inside the business. This enables a small piece of the technical platform (a microservice) to be managed by a small team of software engineers, who collaborate closely with subject-matter experts and business stakeholders within a very specific domain of the business. Such close collaboration can foster better alignment between business goals and the software produced by engineering teams.\\n\\n### Moving Faster\\n\\nMicroservices can allow developers to move faster by enabling continuous delivery and independent deployment of services. In a monolithic architecture, releasing a new feature or fixing a bug typically requires redeploying the entire application. Since microservices allow smaller pieces of your application to be deployed independently, engineering teams can iterate faster and deliver incremental value to business stakeholders.\\n\\nThese positive effects are amplified by the advent of cloud computing. Since deploying a new application no longer requires buying a physical machine and plugging it into your datacenter but rather just applying a new `Deployment` and `Service` on a Kubernetes cluster, it is now truly feasible for small teams of software engineers to own their application stack from laptop-to-production (obviously, within the guardrails set by the central platform team). Furthermore, cloud computing is a pay-as-you-go (and often even pay-for-what-you-use) expense rather than an up-front cost. Therefore, the dollar cost of infrastructure required to support microservices is much lower today than it would have been before the advent of cloud computing and kubernetes.\\n\\n## Conclusion\\n\\nThe microservice architecture is not just a Twitter-driven buzzword but rather a way of designing system that has several real advantages. For most organizations with over two dozen software engineers, building applications with microservices is not an option but rather a _necessity_. However, those advantages come with a cost.\\n\\nWe will discuss those challenges in next week\'s blog post...in the meantime, though, join our [Community Slack](https://launchpass.com/littlehorsecommunity) to get the latest updates!"},{"id":"littlehorse-0.7-release","metadata":{"permalink":"/blog/littlehorse-0.7-release","source":"@site/blog/2024-01-28-0.7-release.md","title":"Releasing 0.7","description":"Approaching a stable `1.0.0` release.","date":"2024-01-28T00:00:00.000Z","tags":[{"label":"littlehorse","permalink":"/blog/tags/littlehorse"},{"label":"release","permalink":"/blog/tags/release"}],"readingTime":4.535,"hasTruncateMarker":true,"authors":[{"name":"The LittleHorse Council","image_url":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4","imageURL":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],"frontMatter":{"title":"Releasing 0.7","description":"Approaching a stable `1.0.0` release.","slug":"littlehorse-0.7-release","authors":[{"name":"The LittleHorse Council","image_url":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4","imageURL":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],"tags":["littlehorse","release"],"image":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4","hide_table_of_contents":false},"unlisted":false,"prevItem":{"title":"The Promise of Microservices","permalink":"/blog/promise-of-microservices"},"nextItem":{"title":"Releasing 0.5.0","permalink":"/blog/littlehorse-0.5.0-release"}},"content":"We are excited to announce the release of `0.7.2`! \x3c!-- truncate --\x3e This is our last release before we cut `1.0.0`, which will be the first stable and production-ready LittleHorse distribution.\\n\\n## Get Started\\n\\nLittleHorse is free for production use according to the Server-Side Public License!\\n\\nTo get started with LittleHorse OSS, you can:\\n\\n* Visit us on [GitHub](https://github.com/littlehorse-enterprises)\\n* Try our [quickstarts](https://littlehorse.dev/docs/developer-guide/install#installation-and-quickstart) or watch our founder, Colt, go through them in [Java](https://www.youtube.com/watch?v=8Zo_UOStg98&t=6s), [Go](https://www.youtube.com/watch?v=oZQc2ISSZsk), or [Python](https://www.youtube.com/watch?v=l3TZOjfpzTw)\\n* Join our [Slack Community](https://launchpass.com/littlehorse-community) for quick and responsive help!\\n\\nAlso, LittleHorse Enterprises LLC has released its first out our [product-focused website](https://littlehorse.io)! If you\'re still curious and want to learn even more, check out a few of our new in-depth tutorial series on [our YouTube page](https://www.youtube.com/@LittleHorse-ey3vw/featured).\\n\\n## New Features\\n\\nRelease `0.7` introduces many features designed to make your life easier. We plan to write blogs about all of them, so stay tuned!\\n\\n### Administrative Dashboard\\n\\nThe most exciting part of the `0.7.2` release of LittleHorse is the new LH Dashboard, which is an administrative portal into your LittleHorse Cluster. The LH Dashboard lets you check on all of your workflows and tasks and debug everything visually with fine-grained detail. Our quickstarts (see above) have everything you need to get started debugging your workflows with our dashboard.\\n\\nThe LH Dashboard is in the alpha stage, so we appreciate any bug reports or feature requests. Please file them on [our github](https://github.com/littlehorse-enterprises/littlehorse/issues)!\\n\\n### Idempotent Metadata Management\\n\\nManaging your `WfSpec`s and `TaskDef`s just got much easier. Check out our [updated docs](https://littlehorse.dev/docs/developer-guide/grpc/managing-metadata) for tutorials on how to keep your DevOps team happy and seamlessly integrate LittleHorse into your normal application development lifecycle.\\n\\n### Child Workflows\\n\\nWe also added the ability to run a `WfRun` which is a \\"child\\" of another `WfRun`. This allows for some interesting features, most importantly:\\n* Sharing `Variable`s between `WfRun`\'s\\n* Foreign-key relationships between the child and parent `WfRun`\'s.\\n\\nStay tuned for an upcoming blog about _why_ we added that feature. It was guided by our resident Domain-Driven Design expert, Eduwer Camacaro! Here\'s a hint: this feature makes it possible to use LittleHorse Workflows as a native data store for complex business entities. This is a great way to implement the \\"Aggregate Pattern.\\"\\n\\n### Enhanced `SearchWfRun`\\n\\nThe `rpc SearchWfRun` request now has a `repeated VariableMatch variable_filters` field on it. This allows you to filter `WfRun`\'s by the value of one or more `Variable`\'s when searching for them, returning only matching `WfRun`\'s. This is super useful when using a LittleHorse `WfRun` to model a business entity, and you need to do something like \\"find all orders placed by `user-id == john` and `status == OUT_FOR_SHIPPING`\\".\\n\\nIn the past, this was possible using the `rpc SearchVariable` and then back the `WfRunId` out of the `VariableId`; however, that method is a little bit clunky. In reality, our users want to find a `WfRunId` matching certain criteria; they\'re not looking for a `Variable`.\\n\\n## What\'s Next?\\n\\nWe couldn\'t be more excited about what is coming next.\\n\\n### Apache2 Clients\\n\\nSome members of the community have expressed concerns about our clients (SDK\'s + GRPC code) being licensed by the SSPL license. We heard you, and we will update them to the Apache 2.0 License before our `1.0.0` release! The server will remain SSPL.\\n\\n### Tutorials\\n\\nOne of our team members, Sohini, has been hard at work creating video tutorials which will help you get quickly up to speed on advanced LittleHorse concepts. You can find them here on our [YouTube](https://www.youtube.com/@LittleHorse-ey3vw/playlists).\\n\\nAdditionally, our founder has recorded a series of zoom meetings with himself (yes, you read that right...Colt used zoom to record a tutorial video series) going through quickstarts in all of our three SDK\'s. You can find them here in [Java](https://www.youtube.com/watch?v=8Zo_UOStg98&t=6s), [Go](https://www.youtube.com/watch?v=oZQc2ISSZsk), or [Python](https://www.youtube.com/watch?v=l3TZOjfpzTw).\\n\\n### Approaching `1.0.0`\\n\\nWhat\'s missing before `1.0.0`? We have some in-progress features that are already merged to `master` but only partially implemented. If you squint hard enough at our GRPC Api, you might notice that we have support for multi-tenancy and also fine-grained ACL\'s. They are NOT ready for production use as we need to iron out a few wrinkles, but we will have them ready for `1.0.0`. We also are working on an `rpc MigrateWfSpec` which allows you to migrate a running `WfRun` from an older version of a `WfSpec` to a newer version. This is hard work for us but it will be highly useful for our users.\\n\\nAdditionally, we are expanding our end-to-end test coverage to try to shake out as many issues as possible _before_ our users tell us about them. So far, the rate of new bugs that we\'ve discovered has slowed down considerably, which makes us think we are getting close to the quality we expect from our own product.\\n\\nWhat will change when we release `1.0.0`? We will be following [Semantic Versioning](https://semver.org) to the letter, which means we will be paying _super close attention_ to any breaking changes to our API. If we want our users to use us for mission critical workloads, we need to take stability seriously\u2014both in terms of performance and API compatibility.\\n\\nWe will also likely have three minor releases per year, with 12 months of patch support for each minor release. This release schedule is copied from Apache Kafka.\\n\\n### LH Cloud\\n\\nLastly, stay tuned for LittleHorse Cloud! Early access is open. If you would like to sign up for early access to LH Cloud, visit [our website](https://www.littlehorse.io/lh-cloud) or contact `sales@littlehorse.io`."},{"id":"littlehorse-0.5.0-release","metadata":{"permalink":"/blog/littlehorse-0.5.0-release","source":"@site/blog/2023-09-08-0.5.0-release.md","title":"Releasing 0.5.0","description":"Python, For-Each, LH Platform.","date":"2023-09-08T00:00:00.000Z","tags":[{"label":"littlehorse","permalink":"/blog/tags/littlehorse"},{"label":"release","permalink":"/blog/tags/release"}],"readingTime":5.205,"hasTruncateMarker":true,"authors":[{"name":"The LittleHorse Council","image_url":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4","imageURL":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],"frontMatter":{"title":"Releasing 0.5.0","description":"Python, For-Each, LH Platform.","slug":"littlehorse-0.5.0-release","authors":[{"name":"The LittleHorse Council","image_url":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4","imageURL":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],"tags":["littlehorse","release"],"image":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4","hide_table_of_contents":false},"unlisted":false,"prevItem":{"title":"Releasing 0.7","permalink":"/blog/littlehorse-0.7-release"},"nextItem":{"title":"Helm and Kubernetes Operators","permalink":"/blog/helm-and-k8s-operators"}},"content":"We are excited to announce the minor release `0.5.0`. \x3c!-- truncate --\x3e This release is highlighted by:\\n\\n* Alpha support for building `WfSpec`s in Python.\\n* Improved monitoring and health metrics on the LittleHorse Server.\\n* Support for looping over a `JSON_ARR` and launching threads in parallel for each element.\\n* Improved Exception Handling.\\n* Limited early access for LittleHorse Platform.\\n\\nIn this release, we made great strides towards full Python support, improved monitoring and observability, and added the ability to spawn threads in parallel looping over a `JSON_ARR` variable.\\n\\n## Get Started\\n\\nLittleHorse is free for production use according to the Server-Side Public License!\\n\\nTo get started with LittleHorse OSS, you can:\\n\\n* Try our [quickstarts](https://littlehorse.dev/docs/developer-guide/install)\\n* Visit us on [GitHub](https://github.com/littlehorse-enterprises/littlehorse) and give us a :star:!\\n* Download our [docker images](https://gallery.ecr.aws/littlehorse)\\n\\n## New Features\\n\\nWe\'d like to highlight some of the exciting new features in `0.5.0`.\\n\\n### Python `WfSpec` Support\\n\\nOur Python SDK now has full support for building `WfSpec`s! You can check it out at our [quickstart page](/docs/developer-guide/install).\\n\\n### For-Each Suppport\\n\\nThis is a very exciting feature which allows you to iterate over a list and spawn multiple `ThreadRun`s (like threads in a program).\\n\\nTo see it in action, check out our [example](https://github.com/littlehorse-enterprises/littlehorse/tree/master/examples/spawn-thread-foreach) or read the [documentation](https://littlehorse.dev/docs/developer-guide/wfspec-development/child-threads).\\n\\n### Improved Failure Handling\\n\\nThis release introduces a new status for LittleHorse, called `EXCEPTION`. The `EXCEPTION` status differs from the `ERROR` status in the following ways:\\n\\n* `ERROR` means an unexpected _technical_ failure occurred. For example, a `TaskRun` timed out because a third-party API was down.\\n* `EXCEPTION` means that a failure occurred at the _business process level_. For example, you might use an `EXCEPTION` when a customer has insufficient funds in her account to complete an order.\\n\\nJust like in programming, you can throw and catch `EXCEPTION`s (and you can also catch `ERROR`s). For a blog post that goes in-depth into how LittleHorse makes it easy to handle failures in your workflows, check out our [Failure Handling Docs](/docs/concepts/workflows#failure-handling).\\n\\n### LH Server Monitoring\\n\\nWe added a new path `/status` on the LH Server\'s health endpoint (port `1822` by default) which can be used to inspect the status of all internal Kafka Streams `Task`s on the LH Server. It presents the following information:\\n\\n* All Active Tasks on the host\\n* All Standby Tasks on the host\\n* Any ongoing State Restorations on the host\\n\\nAdditionally, we added a `/diskUsage` endpoint which returns the number of bytes of disk space in use by the LH Server.\\n\\nLittleHorse Platform uses these endpoints to intelligently scale, manage, and operate LittleHorse for you.\\n\\nWe are also in the process of writing and implementing a Kafka Improvement Proposal to improve visibility of Standby Tasks, which will allow the LittleHorse Operator (both in LH Platform and LH Cloud) to safely and smoothly scale LittleHorse clusters down without any downtime. Stay tuned in the Kafka developer mailing list!\\n\\n### LH Platform\\n\\nLittleHorse Platform is a Kubernetes Operator that securely manages a LittleHorse cluster for you in your own environment. It seamlessly integrates with your Kubernetes environment, GitOps workflows, and security strategy (TLS, mTLS, OAuth, Cert Manager, Keycloak).\\n\\nLittleHorse Platform is now available for limited early access, and has been installed in one of the largest health insurance companies in the US.\\n\\nTo get started with LittleHorse Platform, please [contact us](https://docs.google.com/forms/d/e/1FAIpQLScXVvTYy4LQnYoFoRKRQ7ppuxe0KgncsDukvm96qKN0pU5TnQ/viewform?usp=sf_link).\\n\\n### Persistent Variables\\n\\nIn LittleHorse `0.2.0` and later, you can search for `Variable`s by their value. For example, if you have a Workflow Specification that defines a variable `email_address`, you can find all Workflow Run\'s where `email_address == \'obiwan@jedi-council.org` by using the `SearchVariable` rpc call.\\n\\nThe problem with `0.2.0`? You need to provide the `wfSpecVersion` in your search request. That means you can only search for a `Variable` if you know the version of the `WfSpec` it came from.\\n\\nRelease `0.4.0` introduced the ability to mark a `Variable` as `persistent`, which means that:\\n* Every future version of the `WfSpec` must have the same variable definition with the same index type.\\n* You can now search for variables with a certain value across _all versions_ of the `WfSpec`.\\n\\nBe on the lookout for an upcoming blog post about using Persistent Variables and a simple backend-for-frontend to build an end-to-end Approval Workflow Application using only LittleHorse!\\n\\n## What\'s Next\\n\\nOver the next few weeks, we plan to:\\n\\n* Add utilities to make it easier to work with the LittleHorse API.\\n* Allow users to throw a Workflow `EXCEPTION` from within the Task Worker SDK (currently, only `ERROR` is supported).\\n* Continue hardening the LittleHorse Server\'s availability and performance story.\\n* Launch limited early accesss for LittleHorse Cloud and LittleHorse UI.\\n\\nTo get started with LittleHorse, head over to our [installation docs](https://littlehorse.dev/docs/developer-guide/install).\\n\\n### What about `0.3.0` and `0.4.0`?\\n\\nWe also released `0.3.0` and `0.4.0` over the past 5 weeks! (And before `0.3.0`, we had a minor patch bugfix on `0.2.1`).\\n\\nThe only thing missing with `0.3.0` and `0.4.0` is a blog post + announcement. That\'s because a lot of the features we included in this announcement were partially-implemented, implemented in some languages and not others, or in the \\"experimental\\" phase at the time of `0.3.0` and `0.4.0`. We accelerated the release of `0.3.0` and `0.4.0` because certain early-access customers requested certain features on an accelerated timeline.\\n\\nAs our API is mostly stable now, we will slow down our release cadence to likely a new `*.x.*` version (a `minor` release in [Semantic Versioning](https://semver.org)) every two months, with security and bugfix patch releases (`*.*.x`) as needed.\\n\\nAdditionally, as we introduce new features, we will start a release changelog document in which we document the level of stability of the new API\'s introduced. For example:\\n* `STABLE`: Any changes to this API before the next [Major Release](https://semver.org) will be backwards compatible. The feature is covered by our integration tests.\\n* `BETA`: We don\'t anticipate any _large breaking changes_ to the feature/API. It is covered by our integration tests, but it _might_ change before the `1.0.0` release.\\n* `EXPERIMENTAL`: Try it out and give us feedback! But you might want to wait a release or two before putting it into production.\\n\\nThe `0.6.0` release notes will include a table of all of our features and their API Stability Level in all four of our SDK\'s."},{"id":"helm-and-k8s-operators","metadata":{"permalink":"/blog/helm-and-k8s-operators","source":"@site/blog/2023-09-01-helm-and-k8s-operators.md","title":"Helm and Kubernetes Operators","description":"To Helm or to Operator?","date":"2023-09-01T00:00:00.000Z","tags":[{"label":"friday-tech-tips","permalink":"/blog/tags/friday-tech-tips"},{"label":"kubernetes","permalink":"/blog/tags/kubernetes"}],"readingTime":5.58,"hasTruncateMarker":true,"authors":[{"name":"Colt McNealy","title":"LittleHorse Council Member","url":"https://www.linkedin.com/in/colt-mcnealy-900b7a148/","image_url":"https://avatars.githubusercontent.com/u/100447728","imageURL":"https://avatars.githubusercontent.com/u/100447728"}],"frontMatter":{"title":"Helm and Kubernetes Operators","description":"To Helm or to Operator?","slug":"helm-and-k8s-operators","authors":[{"name":"Colt McNealy","title":"LittleHorse Council Member","url":"https://www.linkedin.com/in/colt-mcnealy-900b7a148/","image_url":"https://avatars.githubusercontent.com/u/100447728","imageURL":"https://avatars.githubusercontent.com/u/100447728"}],"tags":["friday-tech-tips","kubernetes"],"image":"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4","hide_table_of_contents":false},"unlisted":false,"prevItem":{"title":"Releasing 0.5.0","permalink":"/blog/littlehorse-0.5.0-release"},"nextItem":{"title":"Releasing 0.2.0","permalink":"/blog/littlehorse-0.2.0-release"}},"content":"About [Helm](https://helm.sh) vs Kubernetes Operators.\\n\\n\x3c!-- truncate --\x3e\\n\\n## Helm\\n\\nHelm is like `brew` or `npm` for Kubernetes. There are repositories containing charts, and each chart allows you to install an application into your K8s cluster.\\n\\n### How it Works\\n\\nUnder the hood, Helm works by filling out some templated Kubernetes yaml files with user-provided values, drastically reducing boilerplate and allowing you to deploy a reasonably complex application without the user of the helm chart having to understand too much about how to manage such an application.\\n\\nIn addition to that template-engine functionality, Helm also manages versions of your application. You can use Helm to release a new version of your app (for example, updating the docker image tag) and then quickly roll back to a previous version if you discover a bug. This is great for teams deploying stateless applications such as microservices or [LittleHorse Task Workers](https://littlehorse.dev/docs/concepts/task-workers).\\n\\n### The Good\\n\\nFirst, it is quite simple to write a Helm chart. This means that most DevOps teams can quickly write a helm chart that can be used by microservice teams across their organization.\\n\\nSecondly, Helm is a client library (well, it has been since the removal of Tiller...but that\'s another blog post). Therefore, you don\'t need to run any privileged pods inside the K8s cluster; all you need is a CI server with permission to create the necessary K8s resources.\\n\\n### Limitations\\n\\nUnfortunately, Helm doesn\'t do much beyond initial installation and upgrades. Monitoring, self-healing, autoscaling, certificate rotation, and management of non-Kubernetes resources (eg. Kafka Topics, LittleHorse Task Definitions, AWS LoadBalancers, etc) are some exercises left to the reader, to name just a few.\\n\\n## Operators\\n\\n[Kubernetes Operators](https://operatorframework.io/) are a pattern introduced by Red Hat that intends to capture the knowledge of an expert Site Reliability Engineer (or, more punnily, a software operator) into a program that manages (or operates) a complex application.\\n\\nTo accomplish this, a Kubernetes Operator _extends_ the Kubernetes API to introduce a new resource type that is custom-made for your application. The Operator works in tandem with Kubernetes itself to manage applications of a specific type.\\n\\n### How they Work\\n\\nA Kubernetes Operator has two components:\\n\\n1. A `CustomResourceDefinition`, which defines the extension to the Kubernetes API (including relevant configurations for your application type).\\n2. A Controller, which watches any resources from your Custom Resource Definition and \\"reconciles\\" them.\\n\\nThe `CustomResourceDefinition` can be over-simplified as an Open API (not Open AI) specification for how your custom resource will look. For example, in LittleHorse Platform, the simplest version of a `LHCluster` resource (which creates a, you guessed it, LittleHorse Cluster) is:\\n\\n```yaml\\napiVersion: littlehorse.io/v1\\nkind: LHCluster\\nmetadata:\\n name: hello-littlehorse\\n namespace: lh\\nspec:\\n server:\\n version: \\"0.2.0\\"\\n listeners:\\n - name: internal-k8s\\n type: internal\\n port: 2023\\n replicas: 3\\n storage:\\n volumeSize: \\"10G\\"\\n kafka:\\n strimziClusterRef:\\n clusterName: my-strimzi-kafka-cluster\\n listenerPort: 9093\\n```\\n\\nThe `CustomResourceDefinition` allows you to `kubectl apply -fEXCEPTION
s",id:"content-in-exceptions",level:3},{value:"Multi-Tenancy Improvements",id:"multi-tenancy-improvements",level:3},{value:"Kafka Security Protocol Support",id:"kafka-security-protocol-support",level:3},{value:"LittleHorse Canary",id:"littlehorse-canary",level:3},{value:"Exponential Backoff Retry Policy",id:"exponential-backoff-retry-policy",level:3},{value:"JavaScript Client",id:"javascript-client",level:3},{value:"Bugfixes",id:"bugfixes",level:3},{value:"Looking Forward",id:"looking-forward",level:2}];function h(e){const t={a:"a",code:"code",em:"em",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",ul:"ul",...(0,n.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.code,{children:"0.8"})," release of LittleHorse is out! This pre-1.0 release contains many new features, security enhancements, and performance improvements."]}),"\n",(0,s.jsx)(t.h2,{id:"new-features",children:"New Features"}),"\n",(0,s.jsx)(t.p,{children:"New features in this release cover some edge-cases in workflow development which came up from some initial pilots and internal usage of the platform."}),"\n",(0,s.jsx)(t.h3,{id:"dynamic-task-execution",children:"Dynamic Task Execution"}),"\n",(0,s.jsxs)(t.p,{children:["Before this release, a ",(0,s.jsx)(t.code,{children:"TaskNode"})," had a hard-coded reference to a ",(0,s.jsx)(t.code,{children:"TaskDef"}),". This means that every single ",(0,s.jsx)(t.code,{children:"WfRun"})," that reaches the same ",(0,s.jsx)(t.code,{children:"Node"})," in a ",(0,s.jsx)(t.code,{children:"WfSpec"})," ends up executing the same ",(0,s.jsx)(t.code,{children:"TaskDef"}),"."]}),"\n",(0,s.jsxs)(t.p,{children:["However, in LittleHorse Enterprises LLC's upcoming Control Plane project (a system for dynamically provisioning LittleHorse clusters as a SaaS service), we anticipate a special use-case (which we will blog about this upcoming fall) wherein we need to ",(0,s.jsx)(t.em,{children:"choose"})," which ",(0,s.jsx)(t.code,{children:"TaskDef"})," is executed dynamically at runtime."]}),"\n",(0,s.jsxs)(t.p,{children:["Specifically, depending on an input variable to a ",(0,s.jsx)(t.code,{children:"WfRun"})," (in this case, the ",(0,s.jsx)(t.code,{children:"data-plane-id"})," variable), we need to execute a different ",(0,s.jsx)(t.code,{children:"TaskDef"})," so that the ",(0,s.jsx)(t.code,{children:"TaskRun"})," is executed by a speciific Task Worker in a specific location. We will blog about that use-case later."]}),"\n",(0,s.jsx)(t.h3,{id:"per-thread-failure-handlers",children:"Per-Thread Failure Handlers"}),"\n",(0,s.jsxs)(t.p,{children:["Since the ",(0,s.jsx)(t.code,{children:"0.1.0"})," release of LittleHorse it has been possible to put a ",(0,s.jsx)(t.code,{children:"FailureHandler"})," on any ",(0,s.jsx)(t.code,{children:"Node"}),", such that if the ",(0,s.jsx)(t.code,{children:"NodeRun"})," fails, then a Failure Handler thread is"]}),"\n",(0,s.jsxs)(t.h3,{id:"content-in-exceptions",children:["Content in ",(0,s.jsx)(t.code,{children:"EXCEPTION"}),"s"]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"#714"}),"\n"]}),"\n",(0,s.jsx)(t.h3,{id:"multi-tenancy-improvements",children:"Multi-Tenancy Improvements"}),"\n",(0,s.jsxs)(t.p,{children:["Multi-Tenancy has been quietly under development in the LittleHorse Server since the ",(0,s.jsx)(t.code,{children:"0.6.0"})," release introduced a breaking change to allow for it last October. The ",(0,s.jsx)(t.code,{children:"0.8"})," release continues to progress towards making Multi-Tenancy generally-available."]}),"\n",(0,s.jsx)(t.p,{children:"This release includes two new major features for Multi-Tenancy:"}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsxs)(t.li,{children:["Allowing Python and Go clients to set the ",(0,s.jsx)(t.code,{children:"tenant-id"})," header using ",(0,s.jsx)(t.code,{children:"LHC_TENANT_id"})," (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/704",children:"#704"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Allowing administrative ",(0,s.jsx)(t.code,{children:"Principal"}),"s with admin privileges over multiple ",(0,s.jsx)(t.code,{children:"Tenant"}),"s: (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/679",children:"#679"}),")"]}),"\n"]}),"\n",(0,s.jsxs)(t.p,{children:["Multi-Tenancy and support for authentication + fine-grained ACL's via ",(0,s.jsx)(t.code,{children:"Principal"}),"s has been a labor of love implemented by ",(0,s.jsx)(t.a,{href:"https://github.com/eduwercamacaro",children:"Eduwer Camacaro"}),", who has grown into the role of Grumpy Maintainer of LittleHorse."]}),"\n",(0,s.jsx)(t.h3,{id:"kafka-security-protocol-support",children:"Kafka Security Protocol Support"}),"\n",(0,s.jsxs)(t.p,{children:["Prior to release ",(0,s.jsx)(t.code,{children:"0.8"}),", the LH Server could only access a Kafka cluster with either:"]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"Plaintext access with no security."}),"\n",(0,s.jsx)(t.li,{children:"TLS with no authentication."}),"\n",(0,s.jsx)(t.li,{children:"MTLS security."}),"\n"]}),"\n",(0,s.jsxs)(t.p,{children:["PR ",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/716",children:"#716"})," introduced the following Server configurations:"]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"LHS_KAFKA_SECURITY_PROTOCOL"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"LHS_KAFKA_SASL_MECHANISM"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"LHS_KAFKA_SASL_JAAS_CONFIG"})}),"\n"]}),"\n",(0,s.jsx)(t.p,{children:"This allows for access to any Kafka cluster except those requiring loading custom implementations of callbacks on the client side (for example, using the Strimzi OAuth Plug-in)."}),"\n",(0,s.jsx)(t.p,{children:"It is now possible to run LH with Kafka as:"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"No security (PLAINTEXT)"}),"\n",(0,s.jsx)(t.li,{children:"TLS on the brokers, no authentication (SSL)"}),"\n",(0,s.jsx)(t.li,{children:"MTLS on the brokers (SSL with TRUSTSTORE set)"}),"\n",(0,s.jsx)(t.li,{children:"SASL with any JAAS config (SASL_SSL)"}),"\n",(0,s.jsx)(t.li,{children:"Confluent Cloud."}),"\n"]}),"\n",(0,s.jsx)(t.h3,{id:"littlehorse-canary",children:"LittleHorse Canary"}),"\n",(0,s.jsxs)(t.p,{children:["The LittleHorse Canary was released in early access. Inspired by the ",(0,s.jsx)(t.a,{href:"https://strimzi.io/blog/2021/11/09/canary/",children:"Strimzi Canary"})," for Apache Kafka, the LittleHorse Canary is a system that runs workflows on LittleHorse and reports on the health of the cluster(s) that it is monitoring."]}),"\n",(0,s.jsx)(t.p,{children:"The LH Canary system comprises two components:"}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsx)(t.li,{children:"The Metronome, which runs workflows and sends metric beats to a Kafka topic."}),"\n",(0,s.jsx)(t.li,{children:"The Aggregator, which consumes the metrics beats Kafka topic and aggregates metrics to be exposed to Prometheus and a GRPC API."}),"\n"]}),"\n",(0,s.jsx)(t.p,{children:"The goal of the Canary is to monitor, profile, and benchmark LittleHorse Clusters from the same exact perspective as the clients who use them."}),"\n",(0,s.jsxs)(t.p,{children:["The Canary is the brain child of ",(0,s.jsx)(t.a,{href:"https://github.com/sauljabin",children:"Sa\xfal Pi\xf1a"}),", who is also the author of the popular ",(0,s.jsx)(t.a,{href:"https://github.com/sauljabin/kaskade",children:"Kaskade"})," TUI for Apache Kafka."]}),"\n",(0,s.jsx)(t.h3,{id:"exponential-backoff-retry-policy",children:"Exponential Backoff Retry Policy"}),"\n",(0,s.jsxs)(t.p,{children:["PR (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/707",children:"#707"}),") introduced the ability to configure exponential backoff for ",(0,s.jsx)(t.code,{children:"TaskRun"})," retries. Previously, only immediate retries were supported."]}),"\n",(0,s.jsx)(t.h3,{id:"javascript-client",children:"JavaScript Client"}),"\n",(0,s.jsxs)(t.p,{children:["We published the first version of ",(0,s.jsx)(t.code,{children:"littlehorse-client"})," on NPM ",(0,s.jsx)(t.a,{href:"https://www.npmjs.com/package/littlehorse-client",children:"here"}),". This client contains the ",(0,s.jsx)(t.code,{children:"LHConfig"})," in javascript, which provides access to our LittleHorse GRPC API. Note that we do not yet support a JavaScript Task Worker nor a JavaScript ",(0,s.jsx)(t.code,{children:"WfSpec"})," SDK."]}),"\n",(0,s.jsx)(t.h3,{id:"bugfixes",children:"Bugfixes"}),"\n",(0,s.jsx)(t.p,{children:"In this release, we fixed several bugs:"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Task Worker improperly reported ",(0,s.jsx)(t.code,{children:"EXCEPTION"}),"s and ",(0,s.jsx)(t.code,{children:"ERROR"}),"s when throwing ",(0,s.jsx)(t.code,{children:"LHTaskException"})," (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/pull/738",children:"#738"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes task queue rehydration (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/pull/727",children:"#727"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes the Retention Policy for ",(0,s.jsx)(t.code,{children:"ExternalEventDef"}),"'s (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/724",children:"#724"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes deadlock in Java task worker (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/723",children:"#723"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes concurrency bug with the ",(0,s.jsx)(t.code,{children:"AsyncWaiter"})," in the server (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/719",children:"#719"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes various issues from soak tests (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/706",children:"#706"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes to ",(0,s.jsx)(t.code,{children:"NodeRun"})," lifecycle (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/665",children:"#665"}),")."]}),"\n"]}),"\n",(0,s.jsx)(t.h2,{id:"looking-forward",children:"Looking Forward"}),"\n",(0,s.jsx)(t.p,{children:"We continue to stabilize our API and add features that cover edge cases. Load testing, chaos testing, and soak testing are an ongoing project, and we are working with the Apache Kafka Community on a few bugfixes in the Kafka Streams library which is heavily used in the core of LittleHorse."}),"\n",(0,s.jsxs)(t.p,{children:["Once those action items are resolved, we will make a ",(0,s.jsx)(t.code,{children:"1.0"})," release candidate. However, in the meantime we don't expect any massively-breaking API changes at the protocol level. However, certain syntactical changes may occur in our SDK's (especially Go and Python)."]})]})}function d(e={}){const{wrapper:t}={...(0,n.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>l,x:()=>a});var s=i(6540);const n={},r=s.createContext(n);function l(e){const t=s.useContext(r);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:l(e.components),s.createElement(r.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/665a78e5.302adac1.js b/assets/js/665a78e5.302adac1.js
new file mode 100644
index 000000000..ea85021f7
--- /dev/null
+++ b/assets/js/665a78e5.302adac1.js
@@ -0,0 +1 @@
+"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[470],{5854:(e,t,i)=>{i.r(t),i.d(t,{assets:()=>o,contentTitle:()=>l,default:()=>d,frontMatter:()=>r,metadata:()=>a,toc:()=>c});var s=i(4848),n=i(8453);const r={title:"Releasing 0.8",description:"Hardening Security",slug:"littlehorse-0.8-release",authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],tags:["littlehorse","release"],image:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",hide_table_of_contents:!1},l=void 0,a={permalink:"/blog/littlehorse-0.8-release",source:"@site/blog/2023-03-26-0.8.1-release.md",title:"Releasing 0.8",description:"Hardening Security",date:"2023-03-26T00:00:00.000Z",tags:[{label:"littlehorse",permalink:"/blog/tags/littlehorse"},{label:"release",permalink:"/blog/tags/release"}],readingTime:3.94,hasTruncateMarker:!1,authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",imageURL:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],frontMatter:{title:"Releasing 0.8",description:"Hardening Security",slug:"littlehorse-0.8-release",authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",imageURL:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],tags:["littlehorse","release"],image:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",hide_table_of_contents:!1},unlisted:!1,prevItem:{title:"Releasing 0.2.0",permalink:"/blog/littlehorse-0.2.0-release"}},o={authorsImageUrls:[void 0]},c=[{value:"New Features",id:"new-features",level:2},{value:"Dynamic Task Execution",id:"dynamic-task-execution",level:3},{value:"Per-Thread Failure Handlers",id:"per-thread-failure-handlers",level:3},{value:"Content in EXCEPTION
s",id:"content-in-exceptions",level:3},{value:"Multi-Tenancy Improvements",id:"multi-tenancy-improvements",level:3},{value:"Kafka Security Protocol Support",id:"kafka-security-protocol-support",level:3},{value:"LittleHorse Canary",id:"littlehorse-canary",level:3},{value:"Exponential Backoff Retry Policy",id:"exponential-backoff-retry-policy",level:3},{value:"JavaScript Client",id:"javascript-client",level:3},{value:"Bugfixes",id:"bugfixes",level:3},{value:"Looking Forward",id:"looking-forward",level:2}];function h(e){const t={a:"a",code:"code",em:"em",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",ul:"ul",...(0,n.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.code,{children:"0.8"})," release of LittleHorse is out! This pre-1.0 release contains many new features, security enhancements, and performance improvements."]}),"\n",(0,s.jsx)(t.h2,{id:"new-features",children:"New Features"}),"\n",(0,s.jsx)(t.p,{children:"New features in this release cover some edge-cases in workflow development which came up from some initial pilots and internal usage of the platform."}),"\n",(0,s.jsx)(t.h3,{id:"dynamic-task-execution",children:"Dynamic Task Execution"}),"\n",(0,s.jsxs)(t.p,{children:["Before this release, a ",(0,s.jsx)(t.code,{children:"TaskNode"})," had a hard-coded reference to a ",(0,s.jsx)(t.code,{children:"TaskDef"}),". This means that every single ",(0,s.jsx)(t.code,{children:"WfRun"})," that reaches the same ",(0,s.jsx)(t.code,{children:"Node"})," in a ",(0,s.jsx)(t.code,{children:"WfSpec"})," ends up executing the same ",(0,s.jsx)(t.code,{children:"TaskDef"}),"."]}),"\n",(0,s.jsxs)(t.p,{children:["However, in LittleHorse Enterprises LLC's upcoming Control Plane project (a system for dynamically provisioning LittleHorse clusters as a SaaS service), we anticipate a special use-case (which we will blog about this upcoming fall) wherein we need to ",(0,s.jsx)(t.em,{children:"choose"})," which ",(0,s.jsx)(t.code,{children:"TaskDef"})," is executed dynamically at runtime."]}),"\n",(0,s.jsxs)(t.p,{children:["Specifically, depending on an input variable to a ",(0,s.jsx)(t.code,{children:"WfRun"})," (in this case, the ",(0,s.jsx)(t.code,{children:"data-plane-id"})," variable), we need to execute a different ",(0,s.jsx)(t.code,{children:"TaskDef"})," so that the ",(0,s.jsx)(t.code,{children:"TaskRun"})," is executed by a speciific Task Worker in a specific location. We will blog about that use-case later."]}),"\n",(0,s.jsx)(t.h3,{id:"per-thread-failure-handlers",children:"Per-Thread Failure Handlers"}),"\n",(0,s.jsxs)(t.p,{children:["Since the ",(0,s.jsx)(t.code,{children:"0.1.0"})," release of LittleHorse it has been possible to put a ",(0,s.jsx)(t.code,{children:"FailureHandler"})," on any ",(0,s.jsx)(t.code,{children:"Node"}),", such that if the ",(0,s.jsx)(t.code,{children:"NodeRun"})," fails, then a Failure Handler thread is"]}),"\n",(0,s.jsxs)(t.h3,{id:"content-in-exceptions",children:["Content in ",(0,s.jsx)(t.code,{children:"EXCEPTION"}),"s"]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"#714"}),"\n"]}),"\n",(0,s.jsx)(t.h3,{id:"multi-tenancy-improvements",children:"Multi-Tenancy Improvements"}),"\n",(0,s.jsxs)(t.p,{children:["Multi-Tenancy has been quietly under development in the LittleHorse Server since the ",(0,s.jsx)(t.code,{children:"0.6.0"})," release introduced a breaking change to allow for it last October. The ",(0,s.jsx)(t.code,{children:"0.8"})," release continues to progress towards making Multi-Tenancy generally-available."]}),"\n",(0,s.jsx)(t.p,{children:"This release includes two new major features for Multi-Tenancy:"}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsxs)(t.li,{children:["Allowing Python and Go clients to set the ",(0,s.jsx)(t.code,{children:"tenant-id"})," header using ",(0,s.jsx)(t.code,{children:"LHC_TENANT_id"})," (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/704",children:"#704"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Allowing administrative ",(0,s.jsx)(t.code,{children:"Principal"}),"s with admin privileges over multiple ",(0,s.jsx)(t.code,{children:"Tenant"}),"s: (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/679",children:"#679"}),")"]}),"\n"]}),"\n",(0,s.jsxs)(t.p,{children:["Multi-Tenancy and support for authentication + fine-grained ACL's via ",(0,s.jsx)(t.code,{children:"Principal"}),"s has been a labor of love implemented by ",(0,s.jsx)(t.a,{href:"https://github.com/eduwercamacaro",children:"Eduwer Camacaro"}),", who has grown into the role of Grumpy Maintainer of LittleHorse."]}),"\n",(0,s.jsx)(t.h3,{id:"kafka-security-protocol-support",children:"Kafka Security Protocol Support"}),"\n",(0,s.jsxs)(t.p,{children:["Prior to release ",(0,s.jsx)(t.code,{children:"0.8"}),", the LH Server could only access a Kafka cluster with either:"]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"Plaintext access with no security."}),"\n",(0,s.jsx)(t.li,{children:"TLS with no authentication."}),"\n",(0,s.jsx)(t.li,{children:"MTLS security."}),"\n"]}),"\n",(0,s.jsxs)(t.p,{children:["PR ",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/716",children:"#716"})," introduced the following Server configurations:"]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"LHS_KAFKA_SECURITY_PROTOCOL"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"LHS_KAFKA_SASL_MECHANISM"})}),"\n",(0,s.jsx)(t.li,{children:(0,s.jsx)(t.code,{children:"LHS_KAFKA_SASL_JAAS_CONFIG"})}),"\n"]}),"\n",(0,s.jsx)(t.p,{children:"This allows for access to any Kafka cluster except those requiring loading custom implementations of callbacks on the client side (for example, using the Strimzi OAuth Plug-in)."}),"\n",(0,s.jsx)(t.p,{children:"It is now possible to run LH with Kafka as:"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"No security (PLAINTEXT)"}),"\n",(0,s.jsx)(t.li,{children:"TLS on the brokers, no authentication (SSL)"}),"\n",(0,s.jsx)(t.li,{children:"MTLS on the brokers (SSL with TRUSTSTORE set)"}),"\n",(0,s.jsx)(t.li,{children:"SASL with any JAAS config (SASL_SSL)"}),"\n",(0,s.jsx)(t.li,{children:"Confluent Cloud."}),"\n"]}),"\n",(0,s.jsx)(t.h3,{id:"littlehorse-canary",children:"LittleHorse Canary"}),"\n",(0,s.jsxs)(t.p,{children:["The LittleHorse Canary was released in early access. Inspired by the ",(0,s.jsx)(t.a,{href:"https://strimzi.io/blog/2021/11/09/canary/",children:"Strimzi Canary"})," for Apache Kafka, the LittleHorse Canary is a system that runs workflows on LittleHorse and reports on the health of the cluster(s) that it is monitoring."]}),"\n",(0,s.jsx)(t.p,{children:"The LH Canary system comprises two components:"}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsx)(t.li,{children:"The Metronome, which runs workflows and sends metric beats to a Kafka topic."}),"\n",(0,s.jsx)(t.li,{children:"The Aggregator, which consumes the metrics beats Kafka topic and aggregates metrics to be exposed to Prometheus and a GRPC API."}),"\n"]}),"\n",(0,s.jsx)(t.p,{children:"The goal of the Canary is to monitor, profile, and benchmark LittleHorse Clusters from the same exact perspective as the clients who use them."}),"\n",(0,s.jsxs)(t.p,{children:["The Canary is the brain child of ",(0,s.jsx)(t.a,{href:"https://github.com/sauljabin",children:"Sa\xfal Pi\xf1a"}),", who is also the author of the popular ",(0,s.jsx)(t.a,{href:"https://github.com/sauljabin/kaskade",children:"Kaskade"})," TUI for Apache Kafka."]}),"\n",(0,s.jsx)(t.h3,{id:"exponential-backoff-retry-policy",children:"Exponential Backoff Retry Policy"}),"\n",(0,s.jsxs)(t.p,{children:["PR (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/707",children:"#707"}),") introduced the ability to configure exponential backoff for ",(0,s.jsx)(t.code,{children:"TaskRun"})," retries. Previously, only immediate retries were supported."]}),"\n",(0,s.jsx)(t.h3,{id:"javascript-client",children:"JavaScript Client"}),"\n",(0,s.jsxs)(t.p,{children:["We published the first version of ",(0,s.jsx)(t.code,{children:"littlehorse-client"})," on NPM ",(0,s.jsx)(t.a,{href:"https://www.npmjs.com/package/littlehorse-client",children:"here"}),". This client contains the ",(0,s.jsx)(t.code,{children:"LHConfig"})," in javascript, which provides access to our LittleHorse GRPC API. Note that we do not yet support a JavaScript Task Worker nor a JavaScript ",(0,s.jsx)(t.code,{children:"WfSpec"})," SDK."]}),"\n",(0,s.jsx)(t.h3,{id:"bugfixes",children:"Bugfixes"}),"\n",(0,s.jsx)(t.p,{children:"In this release, we fixed several bugs:"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Task Worker improperly reported ",(0,s.jsx)(t.code,{children:"EXCEPTION"}),"s and ",(0,s.jsx)(t.code,{children:"ERROR"}),"s when throwing ",(0,s.jsx)(t.code,{children:"LHTaskException"})," (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/pull/738",children:"#738"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes task queue rehydration (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/pull/727",children:"#727"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes the Retention Policy for ",(0,s.jsx)(t.code,{children:"ExternalEventDef"}),"'s (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/724",children:"#724"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes deadlock in Java task worker (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/723",children:"#723"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes concurrency bug with the ",(0,s.jsx)(t.code,{children:"AsyncWaiter"})," in the server (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/719",children:"#719"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes various issues from soak tests (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/706",children:"#706"}),")"]}),"\n",(0,s.jsxs)(t.li,{children:["Fixes to ",(0,s.jsx)(t.code,{children:"NodeRun"})," lifecycle (",(0,s.jsx)(t.a,{href:"https://github.com/littlehorse-enterprises/littlehorse/pull/665",children:"#665"}),")."]}),"\n"]}),"\n",(0,s.jsx)(t.h2,{id:"looking-forward",children:"Looking Forward"}),"\n",(0,s.jsx)(t.p,{children:"We continue to stabilize our API and add features that cover edge cases. Load testing, chaos testing, and soak testing are an ongoing project, and we are working with the Apache Kafka Community on a few bugfixes in the Kafka Streams library which is heavily used in the core of LittleHorse."}),"\n",(0,s.jsxs)(t.p,{children:["Once those action items are resolved, we will make a ",(0,s.jsx)(t.code,{children:"1.0"})," release candidate. However, in the meantime we don't expect any massively-breaking API changes at the protocol level. However, certain syntactical changes may occur in our SDK's (especially Go and Python)."]})]})}function d(e={}){const{wrapper:t}={...(0,n.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(h,{...e})}):h(e)}},8453:(e,t,i)=>{i.d(t,{R:()=>l,x:()=>a});var s=i(6540);const n={},r=s.createContext(n);function l(e){const t=s.useContext(r);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:l(e.components),s.createElement(r.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/a86a7ed2.191706d3.js b/assets/js/a86a7ed2.aa7a8cd3.js
similarity index 68%
rename from assets/js/a86a7ed2.191706d3.js
rename to assets/js/a86a7ed2.aa7a8cd3.js
index a5a430c0d..5837d7d12 100644
--- a/assets/js/a86a7ed2.191706d3.js
+++ b/assets/js/a86a7ed2.aa7a8cd3.js
@@ -1 +1 @@
-"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[7173],{3773:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>i,contentTitle:()=>r,default:()=>b,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var s=a(4848),n=a(8453);const o={title:"Releasing 0.2.0",description:"Making workflow development easy again.",slug:"littlehorse-0.2.0-release",authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],tags:["littlehorse","release"],image:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",hide_table_of_contents:!1},r=void 0,l={permalink:"/blog/littlehorse-0.2.0-release",source:"@site/blog/2023-08-30-0.2.0-release.md",title:"Releasing 0.2.0",description:"Making workflow development easy again.",date:"2023-08-30T00:00:00.000Z",tags:[{label:"littlehorse",permalink:"/blog/tags/littlehorse"},{label:"release",permalink:"/blog/tags/release"}],readingTime:3.54,hasTruncateMarker:!0,authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",imageURL:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],frontMatter:{title:"Releasing 0.2.0",description:"Making workflow development easy again.",slug:"littlehorse-0.2.0-release",authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",imageURL:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],tags:["littlehorse","release"],image:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",hide_table_of_contents:!1},unlisted:!1,prevItem:{title:"Helm and Kubernetes Operators",permalink:"/blog/helm-and-k8s-operators"}},i={authorsImageUrls:[void 0]},c=[];function u(e){const t={code:"code",p:"p",...(0,n.R)(),...e.components};return(0,s.jsxs)(t.p,{children:["We are excited to announce the release of ",(0,s.jsx)(t.code,{children:"0.2.0"}),"!"]})}function b(e={}){const{wrapper:t}={...(0,n.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(u,{...e})}):u(e)}},8453:(e,t,a)=>{a.d(t,{R:()=>r,x:()=>l});var s=a(6540);const n={},o=s.createContext(n);function r(e){const t=s.useContext(o);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function l(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:r(e.components),s.createElement(o.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
+"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[7173],{3773:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>i,contentTitle:()=>r,default:()=>b,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var s=a(4848),n=a(8453);const o={title:"Releasing 0.2.0",description:"Making workflow development easy again.",slug:"littlehorse-0.2.0-release",authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],tags:["littlehorse","release"],image:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",hide_table_of_contents:!1},r=void 0,l={permalink:"/blog/littlehorse-0.2.0-release",source:"@site/blog/2023-08-30-0.2.0-release.md",title:"Releasing 0.2.0",description:"Making workflow development easy again.",date:"2023-08-30T00:00:00.000Z",tags:[{label:"littlehorse",permalink:"/blog/tags/littlehorse"},{label:"release",permalink:"/blog/tags/release"}],readingTime:3.54,hasTruncateMarker:!0,authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",imageURL:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],frontMatter:{title:"Releasing 0.2.0",description:"Making workflow development easy again.",slug:"littlehorse-0.2.0-release",authors:[{name:"The LittleHorse Council",image_url:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",imageURL:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4"}],tags:["littlehorse","release"],image:"https://avatars.githubusercontent.com/u/140006313?s=400&u=7bf4c91d92dfe590ac71bb6b4821e1a81aa5b712&v=4",hide_table_of_contents:!1},unlisted:!1,prevItem:{title:"Helm and Kubernetes Operators",permalink:"/blog/helm-and-k8s-operators"},nextItem:{title:"Releasing 0.8",permalink:"/blog/littlehorse-0.8-release"}},i={authorsImageUrls:[void 0]},c=[];function u(e){const t={code:"code",p:"p",...(0,n.R)(),...e.components};return(0,s.jsxs)(t.p,{children:["We are excited to announce the release of ",(0,s.jsx)(t.code,{children:"0.2.0"}),"!"]})}function b(e={}){const{wrapper:t}={...(0,n.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(u,{...e})}):u(e)}},8453:(e,t,a)=>{a.d(t,{R:()=>r,x:()=>l});var s=a(6540);const n={},o=s.createContext(n);function r(e){const t=s.useContext(o);return s.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function l(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:r(e.components),s.createElement(o.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/assets/js/d0f3b970.6ed5a2b0.js b/assets/js/d0f3b970.dc491713.js
similarity index 67%
rename from assets/js/d0f3b970.6ed5a2b0.js
rename to assets/js/d0f3b970.dc491713.js
index f4a4ccb59..f9cf68eee 100644
--- a/assets/js/d0f3b970.6ed5a2b0.js
+++ b/assets/js/d0f3b970.dc491713.js
@@ -1 +1 @@
-"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[7756],{8059:l=>{l.exports=JSON.parse('{"tag":{"label":"littlehorse","permalink":"/blog/tags/littlehorse","allTagsPath":"/blog/tags","count":4,"unlisted":false},"listMetadata":{"permalink":"/blog/tags/littlehorse","page":1,"postsPerPage":10,"totalPages":1,"totalCount":4,"blogDescription":"Blog","blogTitle":"Blog"}}')}}]);
\ No newline at end of file
+"use strict";(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[7756],{8059:l=>{l.exports=JSON.parse('{"tag":{"label":"littlehorse","permalink":"/blog/tags/littlehorse","allTagsPath":"/blog/tags","count":5,"unlisted":false},"listMetadata":{"permalink":"/blog/tags/littlehorse","page":1,"postsPerPage":10,"totalPages":1,"totalCount":5,"blogDescription":"Blog","blogTitle":"Blog"}}')}}]);
\ No newline at end of file
diff --git a/assets/js/main.5f704397.js b/assets/js/main.5f704397.js
new file mode 100644
index 000000000..9b3ac9b70
--- /dev/null
+++ b/assets/js/main.5f704397.js
@@ -0,0 +1,2 @@
+/*! For license information please see main.5f704397.js.LICENSE.txt */
+(self.webpackChunklh_site=self.webpackChunklh_site||[]).push([[8792],{9188:(e,t,n)=>{"use strict";n.d(t,{W:()=>a});var r=n(6540);function a(){return r.createElement("svg",{width:"20",height:"20",className:"DocSearch-Search-Icon",viewBox:"0 0 20 20","aria-hidden":"true"},r.createElement("path",{d:"M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z",stroke:"currentColor",fill:"none",fillRule:"evenodd",strokeLinecap:"round",strokeLinejoin:"round"}))}},8328:(e,t,n)=>{"use strict";n.d(t,{A:()=>p});n(6540);var r=n(3259),a=n.n(r),o=n(4054);const i={"01a85c17":[()=>Promise.all([n.e(1869),n.e(8209)]).then(n.bind(n,9158)),"@theme/BlogTagsListPage",9158],"0bd106d9":[()=>Promise.all([n.e(1869),n.e(9371)]).then(n.bind(n,9084)),"@site/docs/05-developer-guide/08-wfspec-development/02-conditionals.md",9084],"150c26e9":[()=>Promise.all([n.e(1869),n.e(4792)]).then(n.bind(n,6575)),"@site/docs/05-developer-guide/09-grpc/05-managing-metadata.md",6575],17896441:[()=>Promise.all([n.e(1869),n.e(8498),n.e(8401)]).then(n.bind(n,2447)),"@theme/DocItem",2447],"1a4e3797":[()=>Promise.all([n.e(1869),n.e(2138)]).then(n.bind(n,673)),"@theme/SearchPage",673],"1f391b9e":[()=>Promise.all([n.e(1869),n.e(8498),n.e(6061)]).then(n.bind(n,7973)),"@theme/MDXPage",7973],"23f588f5":[()=>n.e(7434).then(n.bind(n,4299)),"@site/blog/2023-08-30-0.2.0-release.md",4299],"2494aaaa":[()=>n.e(6958).then(n.bind(n,1511)),"@site/blog/2024-08-22-promise-of-microservices.md",1511],"24a81a13":[()=>n.e(6848).then(n.bind(n,1170)),"@site/docs/06-operations/03-client-configuration.md",1170],"25003e4f":[()=>n.e(2838).then(n.t.bind(n,4351,19)),"@generated/docusaurus-plugin-content-blog/default/p/blog-archive-80c.json",4351],"2a162317":[()=>n.e(7828).then(n.bind(n,7741)),"@site/docs/08-api.md",7741],"32eeb95a":[()=>n.e(4687).then(n.t.bind(n,9017,19)),"@generated/docusaurus-plugin-content-blog/default/p/blog-tags-release-b5c.json",9017],"36994c47":[()=>n.e(9858).then(n.t.bind(n,5516,19)),"@generated/docusaurus-plugin-content-blog/default/__plugin.json",5516],"393be207":[()=>n.e(4134).then(n.bind(n,6602)),"@site/src/pages/markdown-page.md",6602],"41756ce8":[()=>n.e(9616).then(n.t.bind(n,5205,19)),"@generated/docusaurus-plugin-content-blog/default/p/blog-tags-037.json",5205],"477cfdc2":[()=>n.e(2855).then(n.t.bind(n,1541,19)),"@generated/docusaurus-plugin-content-blog/default/p/blog-tags-tech-trends-480.json",1541],"477ea5a4":[()=>n.e(9993).then(n.bind(n,6256)),"@site/docs/06-operations/02-dashboard-configuration.md",6256],"4781d3d2":[()=>n.e(8892).then(n.bind(n,8969)),"@site/blog/2024-08-22-promise-of-microservices.md?truncated=true",8969],"49cd91b3":[()=>n.e(5808).then(n.bind(n,6829)),"@site/docs/05-developer-guide/05-developer-guide.md",6829],"524a0089":[()=>n.e(4982).then(n.bind(n,2885)),"@site/docs/04-concepts/04-external-events.md",2885],"5263bac3":[()=>n.e(7962).then(n.bind(n,7133)),"@site/docs/05-developer-guide/08-wfspec-development/08-wfspec-development.md",7133],"53a3e6dc":[()=>n.e(6261).then(n.t.bind(n,922,19)),"@generated/docusaurus-plugin-content-blog/default/p/blog-c06.json",922],"54ce5819":[()=>n.e(6629).then(n.bind(n,6801)),"@site/blog/2023-09-01-helm-and-k8s-operators.md?truncated=true",6801],"57cb429f":[()=>n.e(2775).then(n.bind(n,2879)),"@site/blog/2023-09-08-0.5.0-release.md?truncated=true",2879],"59a876ee":[()=>n.e(1751).then(n.t.bind(n,2021,19)),"@generated/docusaurus-plugin-content-blog/default/p/blog-tags-kubernetes-4b9.json",2021],"59f5fc7c":[()=>n.e(9905).then(n.bind(n,6500)),"@site/docs/06-operations/01-server-configuration.md",6500],"5a6d38a2":[()=>Promise.all([n.e(1869),n.e(9291)]).then(n.bind(n,365)),"@site/docs/05-developer-guide/08-wfspec-development/03-mutating-variables.md",365],"5d0b0e70":[()=>n.e(1946).then(n.bind(n,9353)),"@site/docs/04-concepts/04-concepts.md",9353],"5e95c892":[()=>n.e(9647).then(n.bind(n,7121)),"@theme/DocsRoot",7121],"5e9f5e1a":[()=>Promise.resolve().then(n.bind(n,4784)),"@generated/docusaurus.config",4784],"61532f60":[()=>n.e(950).then(n.bind(n,7210)),"@site/blog/2023-03-26-0.8.1-release.md",7210],62796615:[()=>Promise.all([n.e(1869),n.e(9445)]).then(n.bind(n,6527)),"@site/docs/05-developer-guide/09-grpc/10-running-workflows.md",6527],"665a78e5":[()=>n.e(470).then(n.bind(n,5854)),"@site/blog/2023-03-26-0.8.1-release.md?truncated=true",5854],"66680c26":[()=>n.e(1642).then(n.bind(n,7814)),"@site/docs/04-concepts/30-advanced/00-wfspec-versioning.md",7814],"6875c492":[()=>Promise.all([n.e(1869),n.e(8498),n.e(3242),n.e(4813)]).then(n.bind(n,3069)),"@theme/BlogTagsPostsPage",3069],"6ddfedc5":[()=>Promise.all([n.e(1869),n.e(6928)]).then(n.bind(n,2347)),"@site/docs/05-developer-guide/08-wfspec-development/08-user-tasks.md",2347],"6ed84113":[()=>Promise.all([n.e(1869),n.e(6417)]).then(n.bind(n,8991)),"@site/docs/05-developer-guide/02-client-configuration.md",8991],"7c110bd0":[()=>n.e(6877).then(n.t.bind(n,2614,19)),"@generated/docusaurus-plugin-content-docs/default/p/docs-b5f.json",2614],"7c1945f5":[()=>n.e(8205).then(n.bind(n,7567)),"@site/blog/2023-09-01-helm-and-k8s-operators.md",7567],"7e3e0d30":[()=>n.e(7746).then(n.bind(n,9744)),"@site/docs/06-operations/10-docker-compose/15-three-servers.md",9744],"7f375326":[()=>n.e(1886).then(n.bind(n,1777)),"@site/blog/2024-01-28-0.7-release.md?truncated=true",1777],"80b6e621":[()=>n.e(6309).then(n.bind(n,5269)),"@site/docs/04-concepts/03-tasks.md",5269],"814f3328":[()=>n.e(7472).then(n.t.bind(n,5513,19)),"~blog/default/blog-post-list-prop-default.json",5513],83879120:[()=>Promise.all([n.e(1869),n.e(4723)]).then(n.bind(n,546)),"@site/docs/05-developer-guide/08-wfspec-development/05-interrupts.md",546],"845dc8ad":[()=>n.e(7506).then(n.bind(n,2767)),"@site/docs/04-concepts/13-principals-and-tenants.md",2767],"899b1876":[()=>n.e(7043).then(n.t.bind(n,7e3,19)),"@generated/docusaurus-plugin-content-blog/default/p/blog-tags-friday-tech-tips-ed0.json",7e3],"8bb7c884":[()=>n.e(3133).then(n.bind(n,4467)),"@site/docs/04-concepts/01-workflows.md",4467],"971c8a60":[()=>n.e(3896).then(n.bind(n,4633)),"@site/docs/06-operations/10-docker-compose/10-docker-compose.md",4633],"9a24bfa0":[()=>Promise.all([n.e(1869),n.e(3372)]).then(n.bind(n,7120)),"@site/docs/05-developer-guide/08-wfspec-development/07-child-threads.md",7120],"9b53f530":[()=>n.e(4120).then(n.bind(n,6174)),"@site/docs/06-operations/10-docker-compose/05-confluent-cloud.md",6174],"9e4087bc":[()=>n.e(2711).then(n.bind(n,9331)),"@theme/BlogArchivePage",9331],a06c0eb2:[()=>n.e(300).then(n.bind(n,9749)),"@site/docs/05-developer-guide/03-lhctl.md",9749],a1824316:[()=>Promise.all([n.e(1869),n.e(9045)]).then(n.bind(n,8848)),"@site/docs/05-developer-guide/08-wfspec-development/04-external-events.md",8848],a6aa9e1f:[()=>Promise.all([n.e(1869),n.e(8498),n.e(3242),n.e(7643)]).then(n.bind(n,5124)),"@theme/BlogListPage",5124],a7456010:[()=>n.e(1235).then(n.t.bind(n,8552,19)),"@generated/docusaurus-plugin-content-pages/default/__plugin.json",8552],a7bd4aaa:[()=>n.e(7098).then(n.bind(n,4532)),"@theme/DocVersionRoot",4532],a86a7ed2:[()=>n.e(7173).then(n.bind(n,3773)),"@site/blog/2023-08-30-0.2.0-release.md?truncated=true",3773],a94703ab:[()=>Promise.all([n.e(1869),n.e(9048)]).then(n.bind(n,2559)),"@theme/DocRoot",2559],aba21aa0:[()=>n.e(5742).then(n.t.bind(n,7093,19)),"@generated/docusaurus-plugin-content-docs/default/__plugin.json",7093],acecf23e:[()=>n.e(1903).then(n.t.bind(n,1912,19)),"~blog/default/blogMetadata-default.json",1912],b23d33c8:[()=>n.e(1561).then(n.bind(n,4941)),"@site/docs/04-concepts/05-user-tasks.md",4941],bacda51b:[()=>n.e(8898).then(n.t.bind(n,8590,19)),"@generated/docusaurus-plugin-content-blog/default/p/blog-tags-microservices-0a7.json",8590],be5744c7:[()=>n.e(8046).then(n.bind(n,7720)),"@site/docs/04-concepts/30-advanced/30-advanced.md",7720],bead5408:[()=>n.e(3484).then(n.bind(n,4721)),"@site/docs/05-developer-guide/09-grpc/09-grpc.md",4721],c141421f:[()=>n.e(957).then(n.t.bind(n,936,19)),"@generated/docusaurus-theme-search-algolia/default/__plugin.json",936],c4f5d8e4:[()=>Promise.all([n.e(1869),n.e(2634)]).then(n.bind(n,2083)),"@site/src/pages/index.js",2083],c69aaf89:[()=>Promise.all([n.e(1869),n.e(3361)]).then(n.bind(n,9686)),"@site/docs/05-developer-guide/09-grpc/20-user-tasks.md",9686],c9736c35:[()=>Promise.all([n.e(1869),n.e(1872)]).then(n.bind(n,5724)),"@site/docs/05-developer-guide/05-task-worker-development.md",5724],ccc49370:[()=>Promise.all([n.e(1869),n.e(8498),n.e(3242),n.e(3249)]).then(n.bind(n,3858)),"@theme/BlogPostPage",3858],d0f3b970:[()=>n.e(7756).then(n.t.bind(n,8059,19)),"@generated/docusaurus-plugin-content-blog/default/p/blog-tags-littlehorse-166.json",8059],d52af3ba:[()=>n.e(9403).then(n.bind(n,8682)),"@site/docs/06-operations/00-overview.md",8682],d5e335f6:[()=>n.e(7130).then(n.bind(n,6e3)),"@site/blog/2023-09-08-0.5.0-release.md",6e3],d61070d0:[()=>n.e(6583).then(n.bind(n,3535)),"@site/blog/2024-01-28-0.7-release.md",3535],d9c5bba9:[()=>n.e(7784).then(n.bind(n,6189)),"@site/docs/06-operations/10-docker-compose/00-basic.md",6189],dd97924f:[()=>Promise.all([n.e(1869),n.e(832)]).then(n.bind(n,635)),"@site/docs/05-developer-guide/09-grpc/00-basics.md",635],ddb4e1f1:[()=>n.e(7948).then(n.bind(n,7811)),"@site/docs/01-overview.md",7811],e33a6d67:[()=>Promise.all([n.e(1869),n.e(3324)]).then(n.bind(n,5329)),"@site/docs/05-developer-guide/08-wfspec-development/01-basics.md",5329],e359e6f5:[()=>Promise.all([n.e(1869),n.e(3983)]).then(n.bind(n,9164)),"@site/docs/05-developer-guide/08-wfspec-development/06-exception-handling.md",9164],eed23361:[()=>Promise.all([n.e(1869),n.e(3234)]).then(n.bind(n,7414)),"@site/docs/02-architecture-and-guarantees.md",7414],f23abd89:[()=>Promise.all([n.e(1869),n.e(216)]).then(n.bind(n,5010)),"@site/docs/05-developer-guide/00-install.md",5010],f447dd38:[()=>Promise.all([n.e(1869),n.e(4635)]).then(n.bind(n,6572)),"@site/docs/05-developer-guide/09-grpc/15-posting-external-events.md",6572],fb7e2344:[()=>n.e(7142).then(n.bind(n,5469)),"@site/docs/06-operations/06-operations.md",5469]};var l=n(4848);function s(e){let{error:t,retry:n,pastDelay:r}=e;return t?(0,l.jsxs)("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"},children:[(0,l.jsx)("p",{children:String(t)}),(0,l.jsx)("div",{children:(0,l.jsx)("button",{type:"button",onClick:n,children:"Retry"})})]}):r?(0,l.jsx)("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"},children:(0,l.jsx)("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb",children:(0,l.jsxs)("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2",children:[(0,l.jsxs)("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0",children:[(0,l.jsx)("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),(0,l.jsx)("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),(0,l.jsx)("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})]}),(0,l.jsxs)("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0",children:[(0,l.jsx)("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),(0,l.jsx)("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),(0,l.jsx)("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})]}),(0,l.jsx)("circle",{cx:"22",cy:"22",r:"8",children:(0,l.jsx)("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"})})]})})}):null}var c=n(6921),u=n(3102);function d(e,t){if("*"===e)return a()({loading:s,loader:()=>n.e(2237).then(n.bind(n,2237)),modules:["@theme/NotFound"],webpack:()=>[2237],render(e,t){const n=e.default;return(0,l.jsx)(u.W,{value:{plugin:{name:"native",id:"default"}},children:(0,l.jsx)(n,{...t})})}});const r=o[`${e}-${t}`],d={},p=[],f=[],g=(0,c.A)(r);return Object.entries(g).forEach((e=>{let[t,n]=e;const r=i[n];r&&(d[t]=r[0],p.push(r[1]),f.push(r[2]))})),a().Map({loading:s,loader:d,modules:p,webpack:()=>f,render(t,n){const a=JSON.parse(JSON.stringify(r));Object.entries(t).forEach((t=>{let[n,r]=t;const o=r.default;if(!o)throw new Error(`The page component at ${e} doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.`);"object"!=typeof o&&"function"!=typeof o||Object.keys(r).filter((e=>"default"!==e)).forEach((e=>{o[e]=r[e]}));let i=a;const l=n.split(".");l.slice(0,-1).forEach((e=>{i=i[e]})),i[l[l.length-1]]=o}));const o=a.__comp;delete a.__comp;const i=a.__context;delete a.__context;const s=a.__props;return delete a.__props,(0,l.jsx)(u.W,{value:i,children:(0,l.jsx)(o,{...a,...s,...n})})}})}const p=[{path:"/blog",component:d("/blog","d2e"),exact:!0},{path:"/blog/archive",component:d("/blog/archive","245"),exact:!0},{path:"/blog/helm-and-k8s-operators",component:d("/blog/helm-and-k8s-operators","905"),exact:!0},{path:"/blog/littlehorse-0.2.0-release",component:d("/blog/littlehorse-0.2.0-release","e1a"),exact:!0},{path:"/blog/littlehorse-0.5.0-release",component:d("/blog/littlehorse-0.5.0-release","19d"),exact:!0},{path:"/blog/littlehorse-0.7-release",component:d("/blog/littlehorse-0.7-release","ca0"),exact:!0},{path:"/blog/littlehorse-0.8-release",component:d("/blog/littlehorse-0.8-release","075"),exact:!0},{path:"/blog/promise-of-microservices",component:d("/blog/promise-of-microservices","920"),exact:!0},{path:"/blog/tags",component:d("/blog/tags","4c4"),exact:!0},{path:"/blog/tags/friday-tech-tips",component:d("/blog/tags/friday-tech-tips","a54"),exact:!0},{path:"/blog/tags/kubernetes",component:d("/blog/tags/kubernetes","f69"),exact:!0},{path:"/blog/tags/littlehorse",component:d("/blog/tags/littlehorse","638"),exact:!0},{path:"/blog/tags/microservices",component:d("/blog/tags/microservices","dd6"),exact:!0},{path:"/blog/tags/release",component:d("/blog/tags/release","f4f"),exact:!0},{path:"/blog/tags/tech-trends",component:d("/blog/tags/tech-trends","8b4"),exact:!0},{path:"/markdown-page",component:d("/markdown-page","3d7"),exact:!0},{path:"/search",component:d("/search","5de"),exact:!0},{path:"/docs",component:d("/docs","cb2"),routes:[{path:"/docs",component:d("/docs","3e2"),routes:[{path:"/docs",component:d("/docs","f63"),routes:[{path:"/docs/api",component:d("/docs/api","8dc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/architecture-and-guarantees",component:d("/docs/architecture-and-guarantees","a57"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/concepts/",component:d("/docs/concepts/","05c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/concepts/advanced/",component:d("/docs/concepts/advanced/","1aa"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/concepts/advanced/wfspec-versioning",component:d("/docs/concepts/advanced/wfspec-versioning","c97"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/concepts/external-events",component:d("/docs/concepts/external-events","0bd"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/concepts/principals-and-tenants",component:d("/docs/concepts/principals-and-tenants","18d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/concepts/tasks",component:d("/docs/concepts/tasks","6d5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/concepts/user-tasks",component:d("/docs/concepts/user-tasks","648"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/concepts/workflows",component:d("/docs/concepts/workflows","2dd"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/",component:d("/docs/developer-guide/","30e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/client-configuration",component:d("/docs/developer-guide/client-configuration","238"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/grpc/",component:d("/docs/developer-guide/grpc/","297"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/grpc/basics",component:d("/docs/developer-guide/grpc/basics","1e2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/grpc/managing-metadata",component:d("/docs/developer-guide/grpc/managing-metadata","d01"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/grpc/posting-external-events",component:d("/docs/developer-guide/grpc/posting-external-events","57f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/grpc/running-workflows",component:d("/docs/developer-guide/grpc/running-workflows","dc9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/grpc/user-tasks",component:d("/docs/developer-guide/grpc/user-tasks","be7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/install",component:d("/docs/developer-guide/install","d47"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/lhctl",component:d("/docs/developer-guide/lhctl","809"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/task-worker-development",component:d("/docs/developer-guide/task-worker-development","168"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/wfspec-development/",component:d("/docs/developer-guide/wfspec-development/","db2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/wfspec-development/basics",component:d("/docs/developer-guide/wfspec-development/basics","801"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/wfspec-development/child-threads",component:d("/docs/developer-guide/wfspec-development/child-threads","663"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/wfspec-development/conditionals",component:d("/docs/developer-guide/wfspec-development/conditionals","576"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/wfspec-development/exception-handling",component:d("/docs/developer-guide/wfspec-development/exception-handling","fde"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/wfspec-development/external-events",component:d("/docs/developer-guide/wfspec-development/external-events","302"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/wfspec-development/interrupts",component:d("/docs/developer-guide/wfspec-development/interrupts","d53"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/wfspec-development/mutating-variables",component:d("/docs/developer-guide/wfspec-development/mutating-variables","da1"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/developer-guide/wfspec-development/user-tasks",component:d("/docs/developer-guide/wfspec-development/user-tasks","8ee"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/operations/",component:d("/docs/operations/","99e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/operations/client-configuration",component:d("/docs/operations/client-configuration","6ce"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/operations/dashboard-configuration",component:d("/docs/operations/dashboard-configuration","138"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/operations/docker-compose/",component:d("/docs/operations/docker-compose/","691"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/operations/docker-compose/basic",component:d("/docs/operations/docker-compose/basic","cbb"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/operations/docker-compose/confluent-cloud",component:d("/docs/operations/docker-compose/confluent-cloud","7a0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/operations/docker-compose/three-servers",component:d("/docs/operations/docker-compose/three-servers","813"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/operations/overview",component:d("/docs/operations/overview","2ba"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/operations/server-configuration",component:d("/docs/operations/server-configuration","43c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/docs/overview",component:d("/docs/overview","904"),exact:!0,sidebar:"tutorialSidebar"}]}]}]},{path:"/",component:d("/","2e1"),exact:!0},{path:"*",component:d("*")}]},6125:(e,t,n)=>{"use strict";n.d(t,{o:()=>o,x:()=>i});var r=n(6540),a=n(4848);const o=r.createContext(!1);function i(e){let{children:t}=e;const[n,i]=(0,r.useState)(!1);return(0,r.useEffect)((()=>{i(!0)}),[]),(0,a.jsx)(o.Provider,{value:n,children:t})}},8536:(e,t,n)=>{"use strict";var r=n(6540),a=n(5338),o=n(4625),i=n(545),l=n(8193);const s=[n(296),n(119),n(6134),n(6294),n(1043)];var c=n(8328),u=n(6347),d=n(2831),p=n(4848);function f(e){let{children:t}=e;return(0,p.jsx)(p.Fragment,{children:t})}var g=n(5260),h=n(4586),m=n(6025),b=n(6342),y=n(5500),v=n(2131),w=n(4090),k=n(2967),x=n(440),S=n(1463);function E(){const{i18n:{currentLocale:e,defaultLocale:t,localeConfigs:n}}=(0,h.A)(),r=(0,v.o)(),a=n[e].htmlLang,o=e=>e.replace("-","_");return(0,p.jsxs)(g.A,{children:[Object.entries(n).map((e=>{let[t,{htmlLang:n}]=e;return(0,p.jsx)("link",{rel:"alternate",href:r.createUrl({locale:t,fullyQualified:!0}),hrefLang:n},t)})),(0,p.jsx)("link",{rel:"alternate",href:r.createUrl({locale:t,fullyQualified:!0}),hrefLang:"x-default"}),(0,p.jsx)("meta",{property:"og:locale",content:o(a)}),Object.values(n).filter((e=>a!==e.htmlLang)).map((e=>(0,p.jsx)("meta",{property:"og:locale:alternate",content:o(e.htmlLang)},`meta-og-${e.htmlLang}`)))]})}function _(e){let{permalink:t}=e;const{siteConfig:{url:n}}=(0,h.A)(),r=function(){const{siteConfig:{url:e,baseUrl:t,trailingSlash:n}}=(0,h.A)(),{pathname:r}=(0,u.zy)();return e+(0,x.applyTrailingSlash)((0,m.A)(r),{trailingSlash:n,baseUrl:t})}(),a=t?`${n}${t}`:r;return(0,p.jsxs)(g.A,{children:[(0,p.jsx)("meta",{property:"og:url",content:a}),(0,p.jsx)("link",{rel:"canonical",href:a})]})}function C(){const{i18n:{currentLocale:e}}=(0,h.A)(),{metadata:t,image:n}=(0,b.p)();return(0,p.jsxs)(p.Fragment,{children:[(0,p.jsxs)(g.A,{children:[(0,p.jsx)("meta",{name:"twitter:card",content:"summary_large_image"}),(0,p.jsx)("body",{className:w.w})]}),n&&(0,p.jsx)(y.be,{image:n}),(0,p.jsx)(_,{}),(0,p.jsx)(E,{}),(0,p.jsx)(S.A,{tag:k.Cy,locale:e}),(0,p.jsx)(g.A,{children:t.map(((e,t)=>(0,p.jsx)("meta",{...e},t)))})]})}const T=new Map;var A=n(6125),j=n(6988),P=n(205);function N(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;rYour Docusaurus site did not load properly.
\nA very common reason is a wrong site baseUrl configuration.
\nCurrent configured baseUrl = ${e} ${"/"===e?" (default value)":""}
\nWe suggest trying baseUrl =
\n{const o=t.toLowerCase(),i=((e,t)=>{const[n,r]=(0,u.useState)(K(t,e)),a=(0,u.useRef)(),o=(0,u.useRef)();return(0,u.useEffect)((()=>{t===a.current&&e===o.current||(a.current=t,o.current=e,r(K(t,e)))}),[e,t]),n})(o,r),l=(e=>(0,u.useCallback)((t=>{var n=t,{className:r,style:a,line:o}=n,i=_(n,["className","style","line"]);const l=E(S({},i),{className:(0,d.A)("token-line",r)});return"object"==typeof e&&"plain"in e&&(l.style=e.plain),"object"==typeof a&&(l.style=S(S({},l.style||{}),a)),l}),[e]))(i),s=(e=>{const t=(0,u.useCallback)((({types:t,empty:n})=>{if(null!=e)return 1===t.length&&"plain"===t[0]?null!=n?{display:"inline-block"}:void 0:1===t.length&&null!=n?e[t[0]]:Object.assign(null!=n?{display:"inline-block"}:{},...t.map((t=>e[t])))}),[e]);return(0,u.useCallback)((e=>{var n=e,{token:r,className:a,style:o}=n,i=_(n,["token","className","style"]);const l=E(S({},i),{className:(0,d.A)("token",...r.types,a),children:r.content,style:t(r)});return null!=o&&(l.style=S(S({},l.style||{}),o)),l}),[t])})(i),c=(({prism:e,code:t,grammar:n,language:r})=>{const a=(0,u.useRef)(e);return(0,u.useMemo)((()=>{if(null==n)return X([t]);const e={code:t,grammar:n,language:r,tokens:[]};return a.current.hooks.run("before-tokenize",e),e.tokens=a.current.tokenize(t,n),a.current.hooks.run("after-tokenize",e),X(e.tokens)}),[t,n,r])})({prism:a,language:o,code:n,grammar:a.languages[o]});return e({tokens:c,className:`prism-code language-${o}`,style:null!=i?i.root:{},getLineProps:l,getTokenProps:s})},ee=e=>(0,u.createElement)(J,E(S({},e),{prism:e.prism||T,theme:e.theme||U,code:e.code,language:e.language}))},5066:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t Your Docusaurus site did not load properly. A very common reason is a wrong site baseUrl configuration. Current configured baseUrl = ${e} ${"/"===e?" (default value)":""} We suggest trying baseUrl = {const o=t.toLowerCase(),i=((e,t)=>{const[n,r]=(0,u.useState)(K(t,e)),a=(0,u.useRef)(),o=(0,u.useRef)();return(0,u.useEffect)((()=>{t===a.current&&e===o.current||(a.current=t,o.current=e,r(K(t,e)))}),[e,t]),n})(o,r),l=(e=>(0,u.useCallback)((t=>{var n=t,{className:r,style:a,line:o}=n,i=_(n,["className","style","line"]);const l=E(S({},i),{className:(0,d.A)("token-line",r)});return"object"==typeof e&&"plain"in e&&(l.style=e.plain),"object"==typeof a&&(l.style=S(S({},l.style||{}),a)),l}),[e]))(i),s=(e=>{const t=(0,u.useCallback)((({types:t,empty:n})=>{if(null!=e)return 1===t.length&&"plain"===t[0]?null!=n?{display:"inline-block"}:void 0:1===t.length&&null!=n?e[t[0]]:Object.assign(null!=n?{display:"inline-block"}:{},...t.map((t=>e[t])))}),[e]);return(0,u.useCallback)((e=>{var n=e,{token:r,className:a,style:o}=n,i=_(n,["token","className","style"]);const l=E(S({},i),{className:(0,d.A)("token",...r.types,a),children:r.content,style:t(r)});return null!=o&&(l.style=S(S({},l.style||{}),o)),l}),[t])})(i),c=(({prism:e,code:t,grammar:n,language:r})=>{const a=(0,u.useRef)(e);return(0,u.useMemo)((()=>{if(null==n)return X([t]);const e={code:t,grammar:n,language:r,tokens:[]};return a.current.hooks.run("before-tokenize",e),e.tokens=a.current.tokenize(t,n),a.current.hooks.run("after-tokenize",e),X(e.tokens)}),[t,n,r])})({prism:a,language:o,code:n,grammar:a.languages[o]});return e({tokens:c,className:`prism-code language-${o}`,style:null!=i?i.root:{},getLineProps:l,getTokenProps:s})},ee=e=>(0,u.createElement)(J,E(S({},e),{prism:e.prism||T,theme:e.theme||U,code:e.code,language:e.language}))},5066:(e,t,n)=>{"use strict";function r(e){var t,n,a="";if("string"==typeof e||"number"==typeof e)a+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t{if(!e)return;const r=t.location.hash;if(r?document.getElementById(r.substring(1)):void 0)return a.current=!0,void n(!1);n(!0)})),{navbarRef:i,isNavbarVisible:t}}(n);return(0,u.jsxs)("nav",{ref:l,"aria-label":(0,s.T)({id:"theme.NavBar.navAriaLabel",message:"Main",description:"The ARIA label for the main navigation"}),className:(0,a.A)("navbar","navbar--fixed-top",n&&[Qe.navbarHideable,!d&&Qe.navbarHidden],{"navbar--dark":"dark"===o,"navbar--primary":"primary"===o,"navbar-sidebar--show":i.shown}),children:[t,(0,u.jsx)(Ze,{onClick:i.toggle}),(0,u.jsx)(Ye,{})]})}var Je=n(440);const et={errorBoundaryError:"errorBoundaryError_a6uf",errorBoundaryFallback:"errorBoundaryFallback_VBag"};function tt(e){return(0,u.jsx)("button",{type:"button",...e,children:(0,u.jsx)(s.A,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again rendering when the React error boundary captures an error",children:"Try again"})})}function nt(e){let{error:t}=e;const n=(0,Je.getErrorCausalChain)(t).map((e=>e.message)).join("\n\nCause:\n");return(0,u.jsx)("p",{className:et.errorBoundaryError,children:n})}class rt extends r.Component{componentDidCatch(e,t){throw this.props.onError(e,t)}render(){return this.props.children}}const at="right";function ot(e){let{width:t=30,height:n=30,className:r,...a}=e;return(0,u.jsx)("svg",{className:r,width:t,height:n,viewBox:"0 0 30 30","aria-hidden":"true",...a,children:(0,u.jsx)("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"})})}function it(){const{toggle:e,shown:t}=(0,j.M)();return(0,u.jsx)("button",{onClick:e,"aria-label":(0,s.T)({id:"theme.docs.sidebar.toggleSidebarButtonAriaLabel",message:"Toggle navigation bar",description:"The ARIA label for hamburger menu button of mobile navigation"}),"aria-expanded":t,className:"navbar__toggle clean-btn",type:"button",children:(0,u.jsx)(ot,{})})}const lt={colorModeToggle:"colorModeToggle_DEke"};function st(e){let{items:t}=e;return(0,u.jsx)(u.Fragment,{children:t.map(((e,t)=>(0,u.jsx)(rt,{onError:t=>new Error(`A theme navbar item failed to render.\nPlease double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config:\n${JSON.stringify(e,null,2)}`,{cause:t}),children:(0,u.jsx)(We,{...e})},t)))})}function ct(e){let{left:t,right:n}=e;return(0,u.jsxs)("div",{className:"navbar__inner",children:[(0,u.jsx)("div",{className:"navbar__items",children:t}),(0,u.jsx)("div",{className:"navbar__items navbar__items--right",children:n})]})}function ut(){const e=(0,j.M)(),t=(0,w.p)().navbar.items,[n,r]=function(e){function t(e){return"left"===(e.position??at)}return[e.filter(t),e.filter((e=>!t(e)))]}(t),a=t.find((e=>"search"===e.type));return(0,u.jsx)(ct,{left:(0,u.jsxs)(u.Fragment,{children:[!e.disabled&&(0,u.jsx)(it,{}),(0,u.jsx)(K,{}),(0,u.jsx)(st,{items:n})]}),right:(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(st,{items:r}),(0,u.jsx)(q,{className:lt.colorModeToggle}),!a&&(0,u.jsx)(ze,{children:(0,u.jsx)(Fe,{})})]})})}function dt(){return(0,u.jsx)(Xe,{children:(0,u.jsx)(ut,{})})}function pt(e){let{item:t}=e;const{to:n,href:r,label:a,prependBaseUrlToHref:o,...i}=t,l=(0,X.A)(n),s=(0,X.A)(r,{forcePrependBaseUrl:!0});return(0,u.jsxs)(Z.A,{className:"footer__link-item",...r?{href:o?s:r}:{to:l},...i,children:[a,r&&!(0,J.A)(r)&&(0,u.jsx)(te.A,{})]})}function ft(e){let{item:t}=e;return t.html?(0,u.jsx)("li",{className:"footer__item",dangerouslySetInnerHTML:{__html:t.html}}):(0,u.jsx)("li",{className:"footer__item",children:(0,u.jsx)(pt,{item:t})},t.href??t.to)}function gt(e){let{column:t}=e;return(0,u.jsxs)("div",{className:"col footer__col",children:[(0,u.jsx)("div",{className:"footer__title",children:t.title}),(0,u.jsx)("ul",{className:"footer__items clean-list",children:t.items.map(((e,t)=>(0,u.jsx)(ft,{item:e},t)))})]})}function ht(e){let{columns:t}=e;return(0,u.jsx)("div",{className:"row footer__links",children:t.map(((e,t)=>(0,u.jsx)(gt,{column:e},t)))})}function mt(){return(0,u.jsx)("span",{className:"footer__link-separator",children:"\xb7"})}function bt(e){let{item:t}=e;return t.html?(0,u.jsx)("span",{className:"footer__link-item",dangerouslySetInnerHTML:{__html:t.html}}):(0,u.jsx)(pt,{item:t})}function yt(e){let{links:t}=e;return(0,u.jsx)("div",{className:"footer__links text--center",children:(0,u.jsx)("div",{className:"footer__links",children:t.map(((e,n)=>(0,u.jsxs)(r.Fragment,{children:[(0,u.jsx)(bt,{item:e}),t.length!==n+1&&(0,u.jsx)(mt,{})]},n)))})})}function vt(e){let{links:t}=e;return function(e){return"title"in e[0]}(t)?(0,u.jsx)(ht,{columns:t}):(0,u.jsx)(yt,{links:t})}var wt=n(1122);const kt={footerLogoLink:"footerLogoLink_BH7S"};function xt(e){let{logo:t}=e;const{withBaseUrl:n}=(0,X.h)(),r={light:n(t.src),dark:n(t.srcDark??t.src)};return(0,u.jsx)(wt.A,{className:(0,a.A)("footer__logo",t.className),alt:t.alt,sources:r,width:t.width,height:t.height,style:t.style})}function St(e){let{logo:t}=e;return t.href?(0,u.jsx)(Z.A,{href:t.href,className:kt.footerLogoLink,target:t.target,children:(0,u.jsx)(xt,{logo:t})}):(0,u.jsx)(xt,{logo:t})}function Et(e){let{copyright:t}=e;return(0,u.jsx)("div",{className:"footer__copyright",dangerouslySetInnerHTML:{__html:t}})}function _t(e){let{style:t,links:n,logo:r,copyright:o}=e;return(0,u.jsx)("footer",{className:(0,a.A)("footer",{"footer--dark":"dark"===t}),children:(0,u.jsxs)("div",{className:"container container-fluid",children:[n,(r||o)&&(0,u.jsxs)("div",{className:"footer__bottom text--center",children:[r&&(0,u.jsx)("div",{className:"margin-bottom--sm",children:r}),o]})]})})}function Ct(){const{footer:e}=(0,w.p)();if(!e)return null;const{copyright:t,links:n,logo:r,style:a}=e;return(0,u.jsx)(_t,{style:a,links:n&&n.length>0&&(0,u.jsx)(vt,{links:n}),logo:r&&(0,u.jsx)(St,{logo:r}),copyright:t&&(0,u.jsx)(Et,{copyright:t})})}const Tt=r.memo(Ct),At=(0,N.fM)([D.a,k.oq,P.Tv,Ue.VQ,i.Jx,function(e){let{children:t}=e;return(0,u.jsx)(L.y_,{children:(0,u.jsx)(j.e,{children:(0,u.jsx)(R,{children:t})})})}]);function jt(e){let{children:t}=e;return(0,u.jsx)(At,{children:t})}var Pt=n(1107);function Nt(e){let{error:t,tryAgain:n}=e;return(0,u.jsx)("main",{className:"container margin-vert--xl",children:(0,u.jsx)("div",{className:"row",children:(0,u.jsxs)("div",{className:"col col--6 col--offset-3",children:[(0,u.jsx)(Pt.A,{as:"h1",className:"hero__title",children:(0,u.jsx)(s.A,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed",children:"This page crashed."})}),(0,u.jsx)("div",{className:"margin-vert--lg",children:(0,u.jsx)(tt,{onClick:n,className:"button button--primary shadow--lw"})}),(0,u.jsx)("hr",{}),(0,u.jsx)("div",{className:"margin-vert--md",children:(0,u.jsx)(nt,{error:t})})]})})})}const Lt={mainWrapper:"mainWrapper_z2l0"};function Ot(e){const{children:t,noFooter:n,wrapperClassName:r,title:l,description:s}=e;return(0,b.J)(),(0,u.jsxs)(jt,{children:[(0,u.jsx)(i.be,{title:l,description:s}),(0,u.jsx)(v,{}),(0,u.jsx)(A,{}),(0,u.jsx)(dt,{}),(0,u.jsx)("div",{id:d,className:(0,a.A)(m.G.wrapper.main,Lt.mainWrapper,r),children:(0,u.jsx)(o.A,{fallback:e=>(0,u.jsx)(Nt,{...e}),children:t})}),!n&&(0,u.jsx)(Tt,{})]})}},3465:(e,t,n)=>{"use strict";n.d(t,{A:()=>u});n(6540);var r=n(8774),a=n(6025),o=n(4586),i=n(6342),l=n(1122),s=n(4848);function c(e){let{logo:t,alt:n,imageClassName:r}=e;const o={light:(0,a.A)(t.src),dark:(0,a.A)(t.srcDark||t.src)},i=(0,s.jsx)(l.A,{className:t.className,sources:o,height:t.height,width:t.width,alt:n,style:t.style});return r?(0,s.jsx)("div",{className:r,children:i}):i}function u(e){const{siteConfig:{title:t}}=(0,o.A)(),{navbar:{title:n,logo:l}}=(0,i.p)(),{imageClassName:u,titleClassName:d,...p}=e,f=(0,a.A)(l?.href||"/"),g=n?"":t,h=l?.alt??g;return(0,s.jsxs)(r.A,{to:f,...p,...l?.target&&{target:l.target},children:[l&&(0,s.jsx)(c,{logo:l,alt:h,imageClassName:u}),null!=n&&(0,s.jsx)("b",{className:d,children:n})]})}},1463:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});n(6540);var r=n(5260),a=n(4848);function o(e){let{locale:t,version:n,tag:o}=e;const i=t;return(0,a.jsxs)(r.A,{children:[t&&(0,a.jsx)("meta",{name:"docusaurus_locale",content:t}),n&&(0,a.jsx)("meta",{name:"docusaurus_version",content:n}),o&&(0,a.jsx)("meta",{name:"docusaurus_tag",content:o}),i&&(0,a.jsx)("meta",{name:"docsearch:language",content:i}),n&&(0,a.jsx)("meta",{name:"docsearch:version",content:n}),o&&(0,a.jsx)("meta",{name:"docsearch:docusaurus_tag",content:o})]})}},1122:(e,t,n)=>{"use strict";n.d(t,{A:()=>u});var r=n(6540),a=n(5066),o=n(2303),i=n(5293);const l={themedComponent:"themedComponent_mlkZ","themedComponent--light":"themedComponent--light_NVdE","themedComponent--dark":"themedComponent--dark_xIcU"};var s=n(4848);function c(e){let{className:t,children:n}=e;const c=(0,o.A)(),{colorMode:u}=(0,i.G)();return(0,s.jsx)(s.Fragment,{children:(c?"dark"===u?["dark"]:["light"]:["light","dark"]).map((e=>{const o=n({theme:e,className:(0,a.A)(t,l.themedComponent,l[`themedComponent--${e}`])});return(0,s.jsx)(r.Fragment,{children:o},e)}))})}function u(e){const{sources:t,className:n,alt:r,...a}=e;return(0,s.jsx)(c,{className:n,children:e=>{let{theme:n,className:o}=e;return(0,s.jsx)("img",{src:t[n],alt:r,className:o,...a})}})}},1422:(e,t,n)=>{"use strict";n.d(t,{N:()=>b,u:()=>c});var r=n(6540),a=n(8193),o=n(205),i=n(3109),l=n(4848);const s="ease-in-out";function c(e){let{initialState:t}=e;const[n,a]=(0,r.useState)(t??!1),o=(0,r.useCallback)((()=>{a((e=>!e))}),[]);return{collapsed:n,setCollapsed:a,toggleCollapsed:o}}const u={display:"none",overflow:"hidden",height:"0px"},d={display:"block",overflow:"visible",height:"auto"};function p(e,t){const n=t?u:d;e.style.display=n.display,e.style.overflow=n.overflow,e.style.height=n.height}function f(e){let{collapsibleRef:t,collapsed:n,animation:a}=e;const o=(0,r.useRef)(!1);(0,r.useEffect)((()=>{const e=t.current;function r(){const t=e.scrollHeight,n=a?.duration??function(e){if((0,i.O)())return 1;const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}(t);return{transition:`height ${n}ms ${a?.easing??s}`,height:`${t}px`}}function l(){const t=r();e.style.transition=t.transition,e.style.height=t.height}if(!o.current)return p(e,n),void(o.current=!0);return e.style.willChange="height",function(){const t=requestAnimationFrame((()=>{n?(l(),requestAnimationFrame((()=>{e.style.height=u.height,e.style.overflow=u.overflow}))):(e.style.display="block",requestAnimationFrame((()=>{l()})))}));return()=>cancelAnimationFrame(t)}()}),[t,n,a])}function g(e){if(!a.A.canUseDOM)return e?u:d}function h(e){let{as:t="div",collapsed:n,children:a,animation:o,onCollapseTransitionEnd:i,className:s,disableSSRStyle:c}=e;const u=(0,r.useRef)(null);return f({collapsibleRef:u,collapsed:n,animation:o}),(0,l.jsx)(t,{ref:u,style:c?void 0:g(n),onTransitionEnd:e=>{"height"===e.propertyName&&(p(u.current,n),i?.(n))},className:s,children:a})}function m(e){let{collapsed:t,...n}=e;const[a,i]=(0,r.useState)(!t),[s,c]=(0,r.useState)(t);return(0,o.A)((()=>{t||i(!0)}),[t]),(0,o.A)((()=>{a&&c(t)}),[a,t]),a?(0,l.jsx)(h,{...n,collapsed:s}):null}function b(e){let{lazy:t,...n}=e;const r=t?m:h;return(0,l.jsx)(r,{...n})}},5041:(e,t,n)=>{"use strict";n.d(t,{Mj:()=>h,oq:()=>g});var r=n(6540),a=n(2303),o=n(9466),i=n(9532),l=n(6342),s=n(4848);const c=(0,o.Wf)("docusaurus.announcement.dismiss"),u=(0,o.Wf)("docusaurus.announcement.id"),d=()=>"true"===c.get(),p=e=>c.set(String(e)),f=r.createContext(null);function g(e){let{children:t}=e;const n=function(){const{announcementBar:e}=(0,l.p)(),t=(0,a.A)(),[n,o]=(0,r.useState)((()=>!!t&&d()));(0,r.useEffect)((()=>{o(d())}),[]);const i=(0,r.useCallback)((()=>{p(!0),o(!0)}),[]);return(0,r.useEffect)((()=>{if(!e)return;const{id:t}=e;let n=u.get();"annoucement-bar"===n&&(n="announcement-bar");const r=t!==n;u.set(t),r&&p(!1),!r&&d()||o(!1)}),[e]),(0,r.useMemo)((()=>({isActive:!!e&&!n,close:i})),[e,n,i])}();return(0,s.jsx)(f.Provider,{value:n,children:t})}function h(){const e=(0,r.useContext)(f);if(!e)throw new i.dV("AnnouncementBarProvider");return e}},5293:(e,t,n)=>{"use strict";n.d(t,{G:()=>b,a:()=>m});var r=n(6540),a=n(8193),o=n(9532),i=n(9466),l=n(6342),s=n(4848);const c=r.createContext(void 0),u="theme",d=(0,i.Wf)(u),p={light:"light",dark:"dark"},f=e=>e===p.dark?p.dark:p.light,g=e=>a.A.canUseDOM?f(document.documentElement.getAttribute("data-theme")):f(e),h=e=>{d.set(f(e))};function m(e){let{children:t}=e;const n=function(){const{colorMode:{defaultMode:e,disableSwitch:t,respectPrefersColorScheme:n}}=(0,l.p)(),[a,o]=(0,r.useState)(g(e));(0,r.useEffect)((()=>{t&&d.del()}),[t]);const i=(0,r.useCallback)((function(t,r){void 0===r&&(r={});const{persist:a=!0}=r;t?(o(t),a&&h(t)):(o(n?window.matchMedia("(prefers-color-scheme: dark)").matches?p.dark:p.light:e),d.del())}),[n,e]);(0,r.useEffect)((()=>{document.documentElement.setAttribute("data-theme",f(a))}),[a]),(0,r.useEffect)((()=>{if(t)return;const e=e=>{if(e.key!==u)return;const t=d.get();null!==t&&i(f(t))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)}),[t,i]);const s=(0,r.useRef)(!1);return(0,r.useEffect)((()=>{if(t&&!n)return;const e=window.matchMedia("(prefers-color-scheme: dark)"),r=()=>{window.matchMedia("print").matches||s.current?s.current=window.matchMedia("print").matches:i(null)};return e.addListener(r),()=>e.removeListener(r)}),[i,t,n]),(0,r.useMemo)((()=>({colorMode:a,setColorMode:i,get isDarkTheme(){return a===p.dark},setLightTheme(){i(p.light)},setDarkTheme(){i(p.dark)}})),[a,i])}();return(0,s.jsx)(c.Provider,{value:n,children:t})}function b(){const e=(0,r.useContext)(c);if(null==e)throw new o.dV("ColorModeProvider","Please see https://docusaurus.io/docs/api/themes/configuration#use-color-mode.");return e}},5597:(e,t,n)=>{"use strict";n.d(t,{VQ:()=>b,XK:()=>w,g1:()=>v});var r=n(6540),a=n(4070),o=n(7065),i=n(6342),l=n(4142),s=n(9532),c=n(9466),u=n(4848);const d=e=>`docs-preferred-version-${e}`,p={save:(e,t,n)=>{(0,c.Wf)(d(e),{persistence:t}).set(n)},read:(e,t)=>(0,c.Wf)(d(e),{persistence:t}).get(),clear:(e,t)=>{(0,c.Wf)(d(e),{persistence:t}).del()}},f=e=>Object.fromEntries(e.map((e=>[e,{preferredVersionName:null}])));const g=r.createContext(null);function h(){const e=(0,a.Gy)(),t=(0,i.p)().docs.versionPersistence,n=(0,r.useMemo)((()=>Object.keys(e)),[e]),[o,l]=(0,r.useState)((()=>f(n)));(0,r.useEffect)((()=>{l(function(e){let{pluginIds:t,versionPersistence:n,allDocsData:r}=e;function a(e){const t=p.read(e,n);return r[e].versions.some((e=>e.name===t))?{preferredVersionName:t}:(p.clear(e,n),{preferredVersionName:null})}return Object.fromEntries(t.map((e=>[e,a(e)])))}({allDocsData:e,versionPersistence:t,pluginIds:n}))}),[e,t,n]);return[o,(0,r.useMemo)((()=>({savePreferredVersion:function(e,n){p.save(e,t,n),l((t=>({...t,[e]:{preferredVersionName:n}})))}})),[t])]}function m(e){let{children:t}=e;const n=h();return(0,u.jsx)(g.Provider,{value:n,children:t})}function b(e){let{children:t}=e;return l.C5?(0,u.jsx)(m,{children:t}):(0,u.jsx)(u.Fragment,{children:t})}function y(){const e=(0,r.useContext)(g);if(!e)throw new s.dV("DocsPreferredVersionContextProvider");return e}function v(e){void 0===e&&(e=o.W);const t=(0,a.ht)(e),[n,i]=y(),{preferredVersionName:l}=n[e];return{preferredVersion:t.versions.find((e=>e.name===l))??null,savePreferredVersionName:(0,r.useCallback)((t=>{i.savePreferredVersion(e,t)}),[i,e])}}function w(){const e=(0,a.Gy)(),[t]=y();function n(n){const r=e[n],{preferredVersionName:a}=t[n];return r.versions.find((e=>e.name===a))??null}const r=Object.keys(e);return Object.fromEntries(r.map((e=>[e,n(e)])))}},6588:(e,t,n)=>{"use strict";n.d(t,{V:()=>s,t:()=>c});var r=n(6540),a=n(9532),o=n(4848);const i=Symbol("EmptyContext"),l=r.createContext(i);function s(e){let{children:t,name:n,items:a}=e;const i=(0,r.useMemo)((()=>n&&a?{name:n,items:a}:null),[n,a]);return(0,o.jsx)(l.Provider,{value:i,children:t})}function c(){const e=(0,r.useContext)(l);if(e===i)throw new a.dV("DocsSidebarProvider");return e}},2252:(e,t,n)=>{"use strict";n.d(t,{n:()=>l,r:()=>s});var r=n(6540),a=n(9532),o=n(4848);const i=r.createContext(null);function l(e){let{children:t,version:n}=e;return(0,o.jsx)(i.Provider,{value:n,children:t})}function s(){const e=(0,r.useContext)(i);if(null===e)throw new a.dV("DocsVersionProvider");return e}},2069:(e,t,n)=>{"use strict";n.d(t,{M:()=>f,e:()=>p});var r=n(6540),a=n(5600),o=n(4581),i=n(7485),l=n(6342),s=n(9532),c=n(4848);const u=r.createContext(void 0);function d(){const e=function(){const e=(0,a.YL)(),{items:t}=(0,l.p)().navbar;return 0===t.length&&!e.component}(),t=(0,o.l)(),n=!e&&"mobile"===t,[s,c]=(0,r.useState)(!1);(0,i.$Z)((()=>{if(s)return c(!1),!1}));const u=(0,r.useCallback)((()=>{c((e=>!e))}),[]);return(0,r.useEffect)((()=>{"desktop"===t&&c(!1)}),[t]),(0,r.useMemo)((()=>({disabled:e,shouldRender:n,toggle:u,shown:s})),[e,n,u,s])}function p(e){let{children:t}=e;const n=d();return(0,c.jsx)(u.Provider,{value:n,children:t})}function f(){const e=r.useContext(u);if(void 0===e)throw new s.dV("NavbarMobileSidebarProvider");return e}},5600:(e,t,n)=>{"use strict";n.d(t,{GX:()=>c,YL:()=>s,y_:()=>l});var r=n(6540),a=n(9532),o=n(4848);const i=r.createContext(null);function l(e){let{children:t}=e;const n=(0,r.useState)({component:null,props:null});return(0,o.jsx)(i.Provider,{value:n,children:t})}function s(){const e=(0,r.useContext)(i);if(!e)throw new a.dV("NavbarSecondaryMenuContentProvider");return e[0]}function c(e){let{component:t,props:n}=e;const o=(0,r.useContext)(i);if(!o)throw new a.dV("NavbarSecondaryMenuContentProvider");const[,l]=o,s=(0,a.Be)(n);return(0,r.useEffect)((()=>{l({component:t,props:s})}),[l,t,s]),(0,r.useEffect)((()=>()=>l({component:null,props:null})),[l]),null}},4090:(e,t,n)=>{"use strict";n.d(t,{w:()=>a,J:()=>o});var r=n(6540);const a="navigation-with-keyboard";function o(){(0,r.useEffect)((()=>{function e(e){"keydown"===e.type&&"Tab"===e.key&&document.body.classList.add(a),"mousedown"===e.type&&document.body.classList.remove(a)}return document.addEventListener("keydown",e),document.addEventListener("mousedown",e),()=>{document.body.classList.remove(a),document.removeEventListener("keydown",e),document.removeEventListener("mousedown",e)}}),[])}},4255:(e,t,n)=>{"use strict";n.d(t,{b:()=>l,w:()=>s});var r=n(6540),a=n(4586),o=n(7485);const i="q";function l(){return(0,o.l)(i)}function s(){const{siteConfig:{baseUrl:e,themeConfig:t}}=(0,a.A)(),{algolia:{searchPagePath:n}}=t;return(0,r.useCallback)((t=>`${e}${n}?${i}=${encodeURIComponent(t)}`),[e,n])}},4581:(e,t,n)=>{"use strict";n.d(t,{l:()=>l});var r=n(6540),a=n(8193);const o={desktop:"desktop",mobile:"mobile",ssr:"ssr"},i=996;function l(e){let{desktopBreakpoint:t=i}=void 0===e?{}:e;const[n,l]=(0,r.useState)((()=>"ssr"));return(0,r.useEffect)((()=>{function e(){l(function(e){if(!a.A.canUseDOM)throw new Error("getWindowSize() should only be called after React hydration");return window.innerWidth>e?o.desktop:o.mobile}(t))}return e(),window.addEventListener("resize",e),()=>{window.removeEventListener("resize",e)}}),[t]),n}},7559:(e,t,n)=>{"use strict";n.d(t,{G:()=>r});const r={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",unlistedBanner:"theme-unlisted-banner",admonitionType:e=>`theme-admonition-${e}`},layout:{},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>`theme-doc-sidebar-item-category-level-${e}`,docSidebarItemLinkLevel:e=>`theme-doc-sidebar-item-link-level-${e}`},blog:{blogFooterTagsRow:"theme-blog-footer-tags-row",blogFooterEditMetaRow:"theme-blog-footer-edit-meta-row"},pages:{pageFooterEditMetaRow:"theme-pages-footer-edit-meta-row"}}},3109:(e,t,n)=>{"use strict";function r(){return window.matchMedia("(prefers-reduced-motion: reduce)").matches}n.d(t,{O:()=>r})},4142:(e,t,n)=>{"use strict";n.d(t,{B5:()=>E,C5:()=>p,Nr:()=>f,OF:()=>w,QB:()=>S,Vd:()=>k,Y:()=>y,fW:()=>x,w8:()=>m});var r=n(6540),a=n(6347),o=n(2831),i=n(4070),l=n(5597),s=n(2252),c=n(6588),u=n(1682),d=n(9169);const p=!!i.Gy;function f(e){return"link"!==e.type||e.unlisted?"category"===e.type?function(e){if(e.href&&!e.linkUnlisted)return e.href;for(const t of e.items){const e=f(t);if(e)return e}}(e):void 0:e.href}const g=(e,t)=>void 0!==e&&(0,d.ys)(e,t),h=(e,t)=>e.some((e=>m(e,t)));function m(e,t){return"link"===e.type?g(e.href,t):"category"===e.type&&(g(e.href,t)||h(e.items,t))}function b(e,t){switch(e.type){case"category":return m(e,t)||e.items.some((e=>b(e,t)));case"link":return!e.unlisted||m(e,t);default:return!0}}function y(e,t){return(0,r.useMemo)((()=>e.filter((e=>b(e,t)))),[e,t])}function v(e){let{sidebarItems:t,pathname:n,onlyCategories:r=!1}=e;const a=[];return function e(t){for(const o of t)if("category"===o.type&&((0,d.ys)(o.href,n)||e(o.items))||"link"===o.type&&(0,d.ys)(o.href,n)){return r&&"category"!==o.type||a.unshift(o),!0}return!1}(t),a}function w(){const e=(0,c.t)(),{pathname:t}=(0,a.zy)(),n=(0,i.vT)()?.pluginData.breadcrumbs;return!1!==n&&e?v({sidebarItems:e.items,pathname:t}):null}function k(e){const{activeVersion:t}=(0,i.zK)(e),{preferredVersion:n}=(0,l.g1)(e),a=(0,i.r7)(e);return(0,r.useMemo)((()=>(0,u.s)([t,n,a].filter(Boolean))),[t,n,a])}function x(e,t){const n=k(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.sidebars?Object.entries(e.sidebars):[])),r=t.find((t=>t[0]===e));if(!r)throw new Error(`Can't find any sidebar with id "${e}" in version${n.length>1?"s":""} ${n.map((e=>e.name)).join(", ")}".\nAvailable sidebar ids are:\n- ${t.map((e=>e[0])).join("\n- ")}`);return r[1]}),[e,n])}function S(e,t){const n=k(t);return(0,r.useMemo)((()=>{const t=n.flatMap((e=>e.docs)),r=t.find((t=>t.id===e));if(!r){if(n.flatMap((e=>e.draftIds)).includes(e))return null;throw new Error(`Couldn't find any doc with id "${e}" in version${n.length>1?"s":""} "${n.map((e=>e.name)).join(", ")}".\nAvailable doc ids are:\n- ${(0,u.s)(t.map((e=>e.id))).join("\n- ")}`)}return r}),[e,n])}function E(e){let{route:t}=e;const n=(0,a.zy)(),r=(0,s.r)(),i=t.routes,l=i.find((e=>(0,a.B6)(n.pathname,e)));if(!l)return null;const c=l.sidebar,u=c?r.docsSidebars[c]:void 0;return{docElement:(0,o.v)(i),sidebarName:c,sidebarItems:u}}},481:(e,t,n)=>{"use strict";n.d(t,{s:()=>a});var r=n(4586);function a(e){const{siteConfig:t}=(0,r.A)(),{title:n,titleDelimiter:a}=t;return e?.trim().length?`${e.trim()} ${a} ${n}`:n}},7485:(e,t,n)=>{"use strict";n.d(t,{$Z:()=>i,aZ:()=>s,l:()=>c});var r=n(6540),a=n(6347),o=n(9532);function i(e){!function(e){const t=(0,a.W6)(),n=(0,o._q)(e);(0,r.useEffect)((()=>t.block(((e,t)=>n(e,t)))),[t,n])}(((t,n)=>{if("POP"===n)return e(t,n)}))}function l(e){const t=(0,a.W6)();return(0,r.useSyncExternalStore)(t.listen,(()=>e(t)),(()=>e(t)))}function s(e){return l((t=>null===e?null:new URLSearchParams(t.location.search).get(e)))}function c(e){const t=s(e)??"",n=function(e){const t=(0,a.W6)();return(0,r.useCallback)(((n,r)=>{const a=new URLSearchParams(t.location.search);n?a.set(e,n):a.delete(e),(r?.push?t.push:t.replace)({search:a.toString()})}),[e,t])}(e);return[t,n]}},1682:(e,t,n)=>{"use strict";function r(e,t){return void 0===t&&(t=(e,t)=>e===t),e.filter(((n,r)=>e.findIndex((e=>t(e,n)))!==r))}function a(e){return Array.from(new Set(e))}n.d(t,{X:()=>r,s:()=>a})},5500:(e,t,n)=>{"use strict";n.d(t,{Jx:()=>f,be:()=>u,e3:()=>p});var r=n(6540),a=n(5066),o=n(5260),i=n(6803),l=n(6025),s=n(481),c=n(4848);function u(e){let{title:t,description:n,keywords:r,image:a,children:i}=e;const u=(0,s.s)(t),{withBaseUrl:d}=(0,l.h)(),p=a?d(a,{absolute:!0}):void 0;return(0,c.jsxs)(o.A,{children:[t&&(0,c.jsx)("title",{children:u}),t&&(0,c.jsx)("meta",{property:"og:title",content:u}),n&&(0,c.jsx)("meta",{name:"description",content:n}),n&&(0,c.jsx)("meta",{property:"og:description",content:n}),r&&(0,c.jsx)("meta",{name:"keywords",content:Array.isArray(r)?r.join(","):r}),p&&(0,c.jsx)("meta",{property:"og:image",content:p}),p&&(0,c.jsx)("meta",{name:"twitter:image",content:p}),i]})}const d=r.createContext(void 0);function p(e){let{className:t,children:n}=e;const i=r.useContext(d),l=(0,a.A)(i,t);return(0,c.jsxs)(d.Provider,{value:l,children:[(0,c.jsx)(o.A,{children:(0,c.jsx)("html",{className:l})}),n]})}function f(e){let{children:t}=e;const n=(0,i.A)(),r=`plugin-${n.plugin.name.replace(/docusaurus-(?:plugin|theme)-(?:content-)?/gi,"")}`;const o=`plugin-id-${n.plugin.id}`;return(0,c.jsx)(p,{className:(0,a.A)(r,o),children:t})}},9532:(e,t,n)=>{"use strict";n.d(t,{Be:()=>c,ZC:()=>l,_q:()=>i,dV:()=>s,fM:()=>u});var r=n(6540),a=n(205),o=n(4848);function i(e){const t=(0,r.useRef)(e);return(0,a.A)((()=>{t.current=e}),[e]),(0,r.useCallback)((function(){return t.current(...arguments)}),[])}function l(e){const t=(0,r.useRef)();return(0,a.A)((()=>{t.current=e})),t.current}class s extends Error{constructor(e,t){super(),this.name="ReactContextError",this.message=`Hook ${this.stack?.split("\n")[1]?.match(/at (?:\w+\.)?(?{"use strict";e.exports=n(5287)},4848:(e,t,n)=>{"use strict";e.exports=n(1020)},7463:(e,t)=>{"use strict";function n(e,t){var n=e.length;e.push(t);e:for(;0|\+|~|\|\|/,punctuation:/[(),]/}},e.languages.css.atrule.inside["selector-function-argument"].inside=t,e.languages.insertBefore("css","property",{variable:{pattern:/(^|[^-\w\xA0-\uFFFF])--(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*/i,lookbehind:!0}}),{pattern:/(\b\d+)(?:%|[a-z]+(?![\w-]))/,lookbehind:!0}),{pattern:/(^|[^\w.-])-?(?:\d+(?:\.\d+)?|\.\d+)/,lookbehind:!0});e.languages.insertBefore("css","function",{operator:{pattern:/(\s)[+\-*\/](?=\s)/,lookbehind:!0},hexcode:{pattern:/\B#[\da-f]{3,8}\b/i,alias:"color"},color:[{pattern:/(^|[^\w-])(?:AliceBlue|AntiqueWhite|Aqua|Aquamarine|Azure|Beige|Bisque|Black|BlanchedAlmond|Blue|BlueViolet|Brown|BurlyWood|CadetBlue|Chartreuse|Chocolate|Coral|CornflowerBlue|Cornsilk|Crimson|Cyan|DarkBlue|DarkCyan|DarkGoldenRod|DarkGr[ae]y|DarkGreen|DarkKhaki|DarkMagenta|DarkOliveGreen|DarkOrange|DarkOrchid|DarkRed|DarkSalmon|DarkSeaGreen|DarkSlateBlue|DarkSlateGr[ae]y|DarkTurquoise|DarkViolet|DeepPink|DeepSkyBlue|DimGr[ae]y|DodgerBlue|FireBrick|FloralWhite|ForestGreen|Fuchsia|Gainsboro|GhostWhite|Gold|GoldenRod|Gr[ae]y|Green|GreenYellow|HoneyDew|HotPink|IndianRed|Indigo|Ivory|Khaki|Lavender|LavenderBlush|LawnGreen|LemonChiffon|LightBlue|LightCoral|LightCyan|LightGoldenRodYellow|LightGr[ae]y|LightGreen|LightPink|LightSalmon|LightSeaGreen|LightSkyBlue|LightSlateGr[ae]y|LightSteelBlue|LightYellow|Lime|LimeGreen|Linen|Magenta|Maroon|MediumAquaMarine|MediumBlue|MediumOrchid|MediumPurple|MediumSeaGreen|MediumSlateBlue|MediumSpringGreen|MediumTurquoise|MediumVioletRed|MidnightBlue|MintCream|MistyRose|Moccasin|NavajoWhite|Navy|OldLace|Olive|OliveDrab|Orange|OrangeRed|Orchid|PaleGoldenRod|PaleGreen|PaleTurquoise|PaleVioletRed|PapayaWhip|PeachPuff|Peru|Pink|Plum|PowderBlue|Purple|RebeccaPurple|Red|RosyBrown|RoyalBlue|SaddleBrown|Salmon|SandyBrown|SeaGreen|SeaShell|Sienna|Silver|SkyBlue|SlateBlue|SlateGr[ae]y|Snow|SpringGreen|SteelBlue|Tan|Teal|Thistle|Tomato|Transparent|Turquoise|Violet|Wheat|White|WhiteSmoke|Yellow|YellowGreen)(?![\w-])/i,lookbehind:!0},{pattern:/\b(?:hsl|rgb)\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*\)\B|\b(?:hsl|rgb)a\(\s*\d{1,3}\s*,\s*\d{1,3}%?\s*,\s*\d{1,3}%?\s*,\s*(?:0|0?\.\d+|1)\s*\)\B/i,inside:{unit:t,number:n,function:/[\w-]+(?=\()/,punctuation:/[(),]/}}],entity:/\\[\da-f]{1,8}/i,unit:t,number:n})}(T),function(e){var t=/[*&][^\s[\]{},]+/,n=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,r="(?:"+n.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+n.source+")?)",a=/(?:[^\s\x00-\x08\x0e-\x1f!"#%&'*,\-:>?@[\]`{|}\x7f-\x84\x86-\x9f\ud800-\udfff\ufffe\uffff]|[?:-]/g,(function(){return n})).replace(/*\/?)?>/.source),e.languages.jsx.tag.inside.tag.pattern=/^<\/?[^\s>\/]*/,e.languages.jsx.tag.inside["attr-value"].pattern=/=(?!\{)(?:"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*'|[^\s'">]+)/,e.languages.jsx.tag.inside.tag.inside["class-name"]=/^[A-Z]\w*(?:\.[A-Z]\w*)*$/,e.languages.jsx.tag.inside.comment=t.comment,e.languages.insertBefore("inside","attr-name",{spread:{pattern:o(/