From dd521aba29eb4b20f3b491ab114c0c589cec91f0 Mon Sep 17 00:00:00 2001 From: Adam Chalmers Date: Fri, 6 Sep 2019 10:42:52 -0500 Subject: [PATCH] TUN-2280: Revert "TUN-2260: add name/group to CapnpConnectParameters, remove Scope" This reverts commit 817c3be9da5465043c2a2fda6c48f7ada760682e. --- cmd/cloudflared/tunnel/cmd.go | 26 +- connection/manager.go | 6 +- tunnelrpc/pogs/json.go | 33 ++ tunnelrpc/pogs/json_test.go | 57 +++ tunnelrpc/pogs/tunnelrpc.go | 89 ++++- tunnelrpc/pogs/tunnelrpc_test.go | 34 +- tunnelrpc/tunnelrpc.capnp | 15 +- tunnelrpc/tunnelrpc.capnp.go | 630 +++++++++++++++++++------------ 8 files changed, 608 insertions(+), 282 deletions(-) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 95085925..2de58774 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -506,19 +506,21 @@ func startDeclarativeTunnel(ctx context.Context, return err } - name := c.String("name") - group := c.String("group") - if group == "" { - err := fmt.Errorf("--group must be specified") - logger.WithError(err).Error("unable to parse group name") + var scope pogs.Scope + if c.IsSet("group") == c.IsSet("system-name") { + err = fmt.Errorf("exactly one of --group or --system-name must be specified") + logger.WithError(err).Error("unable to determine scope") return err + } else if c.IsSet("group") { + scope = pogs.NewGroup(c.String("group")) + } else { + scope = pogs.NewSystemName(c.String("system-name")) } cloudflaredConfig := &connection.CloudflaredConfig{ BuildInfo: buildInfo, CloudflaredID: cloudflaredID, - Name: name, - Group: group, + Scope: scope, Tags: tags, } @@ -942,15 +944,15 @@ func tunnelFlags(shouldHide bool) []cli.Flag { Hidden: true, }), altsrc.NewStringFlag(&cli.StringFlag{ - Name: "name", - Usage: "Friendly name for this cloudflared instance.", - EnvVars: []string{"TUNNEL_DECLARATIVE_NAME"}, + Name: "system-name", + Usage: "Unique identifier for this cloudflared instance. It can be configured individually in the Declarative Tunnel UI. Mutually exclusive with `--group`.", + EnvVars: []string{"TUNNEL_SYSTEM_NAME"}, Hidden: true, }), altsrc.NewStringFlag(&cli.StringFlag{ Name: "group", - Usage: "The group whose behavior this cloudflared instance will adopt. This behavior can be configured by editing the group's 'intent' in the Declarative Tunnel UI.", - EnvVars: []string{"TUNNEL_DECLARATIVE_GROUP"}, + Usage: "Name of a group of cloudflared instances, of which this instance should be an identical copy. They can be configured collectively in the Declarative Tunnel UI. Mutually exclusive with `--system-name`.", + EnvVars: []string{"TUNNEL_GROUP"}, Hidden: true, }), altsrc.NewDurationFlag(&cli.DurationFlag{ diff --git a/connection/manager.go b/connection/manager.go index 7533f4a0..29949062 100644 --- a/connection/manager.go +++ b/connection/manager.go @@ -47,8 +47,7 @@ type CloudflaredConfig struct { CloudflaredID uuid.UUID Tags []pogs.Tag BuildInfo *buildinfo.BuildInfo - Name string - Group string + Scope pogs.Scope } func NewEdgeManager( @@ -137,8 +136,7 @@ func (em *EdgeManager) newConnection(ctx context.Context) error { CloudflaredVersion: em.cloudflaredConfig.BuildInfo.CloudflaredVersion, NumPreviousAttempts: 0, OriginCert: em.state.getUserCredential(), - Name: em.cloudflaredConfig.Name, - Group: em.cloudflaredConfig.Group, + Scope: em.cloudflaredConfig.Scope, Tags: em.cloudflaredConfig.Tags, }, em.logger) if err != nil { diff --git a/tunnelrpc/pogs/json.go b/tunnelrpc/pogs/json.go index 952b1b34..497ee4d8 100644 --- a/tunnelrpc/pogs/json.go +++ b/tunnelrpc/pogs/json.go @@ -8,6 +8,39 @@ import ( "github.com/pkg/errors" ) +// ScopeUnmarshaler can marshal a Scope pog from JSON. +type ScopeUnmarshaler struct { + Scope Scope +} + +// UnmarshalJSON takes in a JSON string, and attempts to marshal it into a Scope. +// If successful, the Scope member of this ScopeUnmarshaler is set and nil is returned. +// If unsuccessful, returns an error. +func (su *ScopeUnmarshaler) UnmarshalJSON(b []byte) error { + var scopeJSON map[string]interface{} + if err := json.Unmarshal(b, &scopeJSON); err != nil { + return errors.Wrapf(err, "cannot unmarshal %s into scopeJSON", string(b)) + } + + if group, ok := scopeJSON["group"]; ok { + if val, ok := group.(string); ok { + su.Scope = NewGroup(val) + return nil + } + return fmt.Errorf("JSON should have been a Scope, but the 'group' key contained %v", group) + } + + if systemName, ok := scopeJSON["system_name"]; ok { + if val, ok := systemName.(string); ok { + su.Scope = NewSystemName(val) + return nil + } + return fmt.Errorf("JSON should have been a Scope, but the 'system_name' key contained %v", systemName) + } + + return fmt.Errorf("JSON should have been an object with one root key, either 'system_name' or 'group'") +} + // OriginConfigJSONHandler is a wrapper to serialize OriginConfig with type information, and deserialize JSON // into an OriginConfig. type OriginConfigJSONHandler struct { diff --git a/tunnelrpc/pogs/json_test.go b/tunnelrpc/pogs/json_test.go index 8d09f8cd..70a896a4 100644 --- a/tunnelrpc/pogs/json_test.go +++ b/tunnelrpc/pogs/json_test.go @@ -9,6 +9,59 @@ import ( "github.com/stretchr/testify/assert" ) +func TestScopeUnmarshaler_UnmarshalJSON(t *testing.T) { + type fields struct { + Scope Scope + } + type args struct { + b []byte + } + tests := []struct { + name string + fields fields + args args + wantErr bool + wantScope Scope + }{ + { + name: "group_successful", + args: args{b: []byte(`{"group": "my-group"}`)}, + wantScope: NewGroup("my-group"), + }, + { + name: "system_name_successful", + args: args{b: []byte(`{"system_name": "my-computer"}`)}, + wantScope: NewSystemName("my-computer"), + }, + { + name: "not_a_scope", + args: args{b: []byte(`{"x": "y"}`)}, + wantErr: true, + }, + { + name: "malformed_group", + args: args{b: []byte(`{"group": ["a", "b"]}`)}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + su := &ScopeUnmarshaler{ + Scope: tt.fields.Scope, + } + err := su.UnmarshalJSON(tt.args.b) + if !tt.wantErr { + if err != nil { + t.Errorf("ScopeUnmarshaler.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + } + if !eqScope(tt.wantScope, su.Scope) { + t.Errorf("Wanted scope %v but got scope %v", tt.wantScope, su.Scope) + } + } + }) + } +} + func TestUnmarshalOrigin(t *testing.T) { tests := []struct { jsonLiteral string @@ -283,3 +336,7 @@ type prettyJSON string func prettyToValidJSON(prettyJSON string) string { return strings.ReplaceAll(strings.ReplaceAll(prettyJSON, "\n", ""), "\t", "") } + +func eqScope(s1, s2 Scope) bool { + return s1.Value() == s2.Value() && s1.PostgresType() == s2.PostgresType() +} diff --git a/tunnelrpc/pogs/tunnelrpc.go b/tunnelrpc/pogs/tunnelrpc.go index bb3953ed..8d41b63a 100644 --- a/tunnelrpc/pogs/tunnelrpc.go +++ b/tunnelrpc/pogs/tunnelrpc.go @@ -2,6 +2,7 @@ package pogs import ( "context" + "fmt" "time" "github.com/cloudflare/cloudflared/tunnelrpc" @@ -128,14 +129,82 @@ func UnmarshalServerInfo(s tunnelrpc.ServerInfo) (*ServerInfo, error) { return p, err } +//go-sumtype:decl Scope +type Scope interface { + Value() string + PostgresType() string + GraphQLType() string + isScope() +} + +type SystemName struct { + systemName string +} + +func NewSystemName(systemName string) *SystemName { + return &SystemName{systemName: systemName} +} + +func (s *SystemName) Value() string { return s.systemName } +func (_ *SystemName) PostgresType() string { return "system_name" } +func (_ *SystemName) GraphQLType() string { return "SYSTEM_NAME" } + +func (_ *SystemName) isScope() {} + +type Group struct { + group string +} + +func NewGroup(group string) *Group { + return &Group{group: group} +} + +func (g *Group) Value() string { return g.group } +func (_ *Group) PostgresType() string { return "group" } +func (_ *Group) GraphQLType() string { return "GROUP" } + +func (_ *Group) isScope() {} + +func MarshalScope(s tunnelrpc.Scope, p Scope) error { + ss := s.Value() + switch scope := p.(type) { + case *SystemName: + ss.SetSystemName(scope.systemName) + case *Group: + ss.SetGroup(scope.group) + default: + return fmt.Errorf("unexpected Scope value: %v", p) + } + return nil +} + +func UnmarshalScope(s tunnelrpc.Scope) (Scope, error) { + ss := s.Value() + switch ss.Which() { + case tunnelrpc.Scope_value_Which_systemName: + systemName, err := ss.SystemName() + if err != nil { + return nil, err + } + return NewSystemName(systemName), nil + case tunnelrpc.Scope_value_Which_group: + group, err := ss.Group() + if err != nil { + return nil, err + } + return NewGroup(group), nil + default: + return nil, fmt.Errorf("unexpected Scope tag: %v", ss.Which()) + } +} + type ConnectParameters struct { OriginCert []byte CloudflaredID uuid.UUID NumPreviousAttempts uint8 Tags []Tag CloudflaredVersion string - Name string - Group string + Scope Scope } func MarshalConnectParameters(s tunnelrpc.CapnpConnectParameters, p *ConnectParameters) error { @@ -168,13 +237,11 @@ func MarshalConnectParameters(s tunnelrpc.CapnpConnectParameters, p *ConnectPara if err := s.SetCloudflaredVersion(p.CloudflaredVersion); err != nil { return err } - if err := s.SetName(p.Name); err != nil { + scope, err := s.NewScope() + if err != nil { return err } - if err := s.SetGroup(p.Group); err != nil { - return err - } - return nil + return MarshalScope(scope, p.Scope) } func UnmarshalConnectParameters(s tunnelrpc.CapnpConnectParameters) (*ConnectParameters, error) { @@ -215,12 +282,11 @@ func UnmarshalConnectParameters(s tunnelrpc.CapnpConnectParameters) (*ConnectPar return nil, err } - name, err := s.Name() + scopeCapnp, err := s.Scope() if err != nil { return nil, err } - - group, err := s.Group() + scope, err := UnmarshalScope(scopeCapnp) if err != nil { return nil, err } @@ -231,8 +297,7 @@ func UnmarshalConnectParameters(s tunnelrpc.CapnpConnectParameters) (*ConnectPar NumPreviousAttempts: s.NumPreviousAttempts(), Tags: tags, CloudflaredVersion: cloudflaredVersion, - Name: name, - Group: group, + Scope: scope, }, nil } diff --git a/tunnelrpc/pogs/tunnelrpc_test.go b/tunnelrpc/pogs/tunnelrpc_test.go index 7daea8bc..b2f2e71e 100644 --- a/tunnelrpc/pogs/tunnelrpc_test.go +++ b/tunnelrpc/pogs/tunnelrpc_test.go @@ -11,6 +11,35 @@ import ( capnp "zombiezen.com/go/capnproto2" ) +// Assert *SystemName implements Scope +var _ Scope = (*SystemName)(nil) + +// Assert *Group implements Scope +var _ Scope = (*Group)(nil) + +func TestScope(t *testing.T) { + testCases := []Scope{ + &SystemName{systemName: "my_system"}, + &Group{group: "my_group"}, + } + for i, testCase := range testCases { + _, seg, err := capnp.NewMessage(capnp.SingleSegment(nil)) + capnpEntity, err := tunnelrpc.NewScope(seg) + if !assert.NoError(t, err) { + t.Fatal("Couldn't initialize a new message") + } + err = MarshalScope(capnpEntity, testCase) + if !assert.NoError(t, err, "testCase index %v failed to marshal", i) { + continue + } + result, err := UnmarshalScope(capnpEntity) + if !assert.NoError(t, err, "testCase index %v failed to unmarshal", i) { + continue + } + assert.Equal(t, testCase, result, "testCase index %v didn't preserve struct through marshalling and unmarshalling", i) + } +} + func sampleTestConnectResult() *ConnectResult { return &ConnectResult{ Err: &ConnectError{ @@ -49,7 +78,7 @@ func TestConnectParameters(t *testing.T) { testCases := []*ConnectParameters{ sampleConnectParameters(), sampleConnectParameters(func(c *ConnectParameters) { - c.Name = "" + c.Scope = &SystemName{systemName: "my_system"} }), sampleConnectParameters(func(c *ConnectParameters) { c.Tags = nil @@ -89,8 +118,7 @@ func sampleConnectParameters(overrides ...func(*ConnectParameters)) *ConnectPara }, }, CloudflaredVersion: "7.0", - Name: "My Computer", - Group: "www", + Scope: &Group{group: "my_group"}, } sample.ensureNoZeroFields() for _, f := range overrides { diff --git a/tunnelrpc/tunnelrpc.capnp b/tunnelrpc/tunnelrpc.capnp index 5ae59456..5beecc18 100644 --- a/tunnelrpc/tunnelrpc.capnp +++ b/tunnelrpc/tunnelrpc.capnp @@ -57,10 +57,17 @@ struct CapnpConnectParameters { tags @3 :List(Tag); # release version of cloudflared cloudflaredVersion @4 :Text; - # friendly name for this cloudflared instance - name @5 :Text; - # group whose behavior this cloudflared instance will adopt - group @6 :Text; + # identifier for this cloudflared instance + scope @5 :Scope; +} + +struct Scope { + value :union { + # Standalone instance + systemName @0 :Text; + # Associated with a group of identical cloudflared instances + group @1 :Text; + } } struct ConnectResult { diff --git a/tunnelrpc/tunnelrpc.capnp.go b/tunnelrpc/tunnelrpc.capnp.go index 175e8e15..0dd6ab0e 100644 --- a/tunnelrpc/tunnelrpc.capnp.go +++ b/tunnelrpc/tunnelrpc.capnp.go @@ -504,12 +504,12 @@ type CapnpConnectParameters struct{ capnp.Struct } const CapnpConnectParameters_TypeID = 0xa78f37418c1077c8 func NewCapnpConnectParameters(s *capnp.Segment) (CapnpConnectParameters, error) { - st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 6}) + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 5}) return CapnpConnectParameters{st}, err } func NewRootCapnpConnectParameters(s *capnp.Segment) (CapnpConnectParameters, error) { - st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 6}) + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 5}) return CapnpConnectParameters{st}, err } @@ -603,42 +603,29 @@ func (s CapnpConnectParameters) SetCloudflaredVersion(v string) error { return s.Struct.SetText(3, v) } -func (s CapnpConnectParameters) Name() (string, error) { +func (s CapnpConnectParameters) Scope() (Scope, error) { p, err := s.Struct.Ptr(4) - return p.Text(), err + return Scope{Struct: p.Struct()}, err } -func (s CapnpConnectParameters) HasName() bool { +func (s CapnpConnectParameters) HasScope() bool { p, err := s.Struct.Ptr(4) return p.IsValid() || err != nil } -func (s CapnpConnectParameters) NameBytes() ([]byte, error) { - p, err := s.Struct.Ptr(4) - return p.TextBytes(), err +func (s CapnpConnectParameters) SetScope(v Scope) error { + return s.Struct.SetPtr(4, v.Struct.ToPtr()) } -func (s CapnpConnectParameters) SetName(v string) error { - return s.Struct.SetText(4, v) -} - -func (s CapnpConnectParameters) Group() (string, error) { - p, err := s.Struct.Ptr(5) - return p.Text(), err -} - -func (s CapnpConnectParameters) HasGroup() bool { - p, err := s.Struct.Ptr(5) - return p.IsValid() || err != nil -} - -func (s CapnpConnectParameters) GroupBytes() ([]byte, error) { - p, err := s.Struct.Ptr(5) - return p.TextBytes(), err -} - -func (s CapnpConnectParameters) SetGroup(v string) error { - return s.Struct.SetText(5, v) +// NewScope sets the scope field to a newly +// allocated Scope struct, preferring placement in s's segment. +func (s CapnpConnectParameters) NewScope() (Scope, error) { + ss, err := NewScope(s.Struct.Segment()) + if err != nil { + return Scope{}, err + } + err = s.Struct.SetPtr(4, ss.Struct.ToPtr()) + return ss, err } // CapnpConnectParameters_List is a list of CapnpConnectParameters. @@ -646,7 +633,7 @@ type CapnpConnectParameters_List struct{ capnp.List } // NewCapnpConnectParameters creates a new list of CapnpConnectParameters. func NewCapnpConnectParameters_List(s *capnp.Segment, sz int32) (CapnpConnectParameters_List, error) { - l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 8, PointerCount: 6}, sz) + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 8, PointerCount: 5}, sz) return CapnpConnectParameters_List{l}, err } @@ -671,6 +658,147 @@ func (p CapnpConnectParameters_Promise) Struct() (CapnpConnectParameters, error) return CapnpConnectParameters{s}, err } +func (p CapnpConnectParameters_Promise) Scope() Scope_Promise { + return Scope_Promise{Pipeline: p.Pipeline.GetPipeline(4)} +} + +type Scope struct{ capnp.Struct } +type Scope_value Scope +type Scope_value_Which uint16 + +const ( + Scope_value_Which_systemName Scope_value_Which = 0 + Scope_value_Which_group Scope_value_Which = 1 +) + +func (w Scope_value_Which) String() string { + const s = "systemNamegroup" + switch w { + case Scope_value_Which_systemName: + return s[0:10] + case Scope_value_Which_group: + return s[10:15] + + } + return "Scope_value_Which(" + strconv.FormatUint(uint64(w), 10) + ")" +} + +// Scope_TypeID is the unique identifier for the type Scope. +const Scope_TypeID = 0xc54a4a6fd4d87596 + +func NewScope(s *capnp.Segment) (Scope, error) { + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 1}) + return Scope{st}, err +} + +func NewRootScope(s *capnp.Segment) (Scope, error) { + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 1}) + return Scope{st}, err +} + +func ReadRootScope(msg *capnp.Message) (Scope, error) { + root, err := msg.RootPtr() + return Scope{root.Struct()}, err +} + +func (s Scope) String() string { + str, _ := text.Marshal(0xc54a4a6fd4d87596, s.Struct) + return str +} + +func (s Scope) Value() Scope_value { return Scope_value(s) } + +func (s Scope_value) Which() Scope_value_Which { + return Scope_value_Which(s.Struct.Uint16(0)) +} +func (s Scope_value) SystemName() (string, error) { + if s.Struct.Uint16(0) != 0 { + panic("Which() != systemName") + } + p, err := s.Struct.Ptr(0) + return p.Text(), err +} + +func (s Scope_value) HasSystemName() bool { + if s.Struct.Uint16(0) != 0 { + return false + } + p, err := s.Struct.Ptr(0) + return p.IsValid() || err != nil +} + +func (s Scope_value) SystemNameBytes() ([]byte, error) { + p, err := s.Struct.Ptr(0) + return p.TextBytes(), err +} + +func (s Scope_value) SetSystemName(v string) error { + s.Struct.SetUint16(0, 0) + return s.Struct.SetText(0, v) +} + +func (s Scope_value) Group() (string, error) { + if s.Struct.Uint16(0) != 1 { + panic("Which() != group") + } + p, err := s.Struct.Ptr(0) + return p.Text(), err +} + +func (s Scope_value) HasGroup() bool { + if s.Struct.Uint16(0) != 1 { + return false + } + p, err := s.Struct.Ptr(0) + return p.IsValid() || err != nil +} + +func (s Scope_value) GroupBytes() ([]byte, error) { + p, err := s.Struct.Ptr(0) + return p.TextBytes(), err +} + +func (s Scope_value) SetGroup(v string) error { + s.Struct.SetUint16(0, 1) + return s.Struct.SetText(0, v) +} + +// Scope_List is a list of Scope. +type Scope_List struct{ capnp.List } + +// NewScope creates a new list of Scope. +func NewScope_List(s *capnp.Segment, sz int32) (Scope_List, error) { + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 8, PointerCount: 1}, sz) + return Scope_List{l}, err +} + +func (s Scope_List) At(i int) Scope { return Scope{s.List.Struct(i)} } + +func (s Scope_List) Set(i int, v Scope) error { return s.List.SetStruct(i, v.Struct) } + +func (s Scope_List) String() string { + str, _ := text.MarshalList(0xc54a4a6fd4d87596, s.List) + return str +} + +// Scope_Promise is a wrapper for a Scope promised by a client call. +type Scope_Promise struct{ *capnp.Pipeline } + +func (p Scope_Promise) Struct() (Scope, error) { + s, err := p.Pipeline.Struct() + return Scope{s}, err +} + +func (p Scope_Promise) Value() Scope_value_Promise { return Scope_value_Promise{p.Pipeline} } + +// Scope_value_Promise is a wrapper for a Scope_value promised by a client call. +type Scope_value_Promise struct{ *capnp.Pipeline } + +func (p Scope_value_Promise) Struct() (Scope_value, error) { + s, err := p.Pipeline.Struct() + return Scope_value{s}, err +} + type ConnectResult struct{ capnp.Struct } // ConnectResult_TypeID is the unique identifier for the type ConnectResult. @@ -3576,223 +3704,229 @@ func (p ClientService_useConfiguration_Results_Promise) Result() UseConfiguratio return UseConfigurationResult_Promise{Pipeline: p.Pipeline.GetPipeline(0)} } -const schema_db8274f9144abc7e = "x\xda\xacY{p\\\xe5u?\xe7~\xab\xbd\x96-" + - "y\xf7r\x15\x88\x84=j<0\x09\x04S\x8cK\x1b" + - "\xd4N\xd6\x92,Gr\xfc\xd8O+\x1b0\xf6\x8c\xaf" + - "w?\xad\xae}\xf7\xde\xf5}\x18\xc9cb\xf0\x98\x02" + - "*\x04C\xf0\x0cv C\xdc\xba<\x06\xca#0\x1d" + - "\xa8\x9d\x92N)\xd0\x94!L\xa0S\xb7\xc9\x1f\x04<" + - "\x0dL2\x04B\x87\xd2\x01n\xe7\xdc\xb7VB\xb6;" + - "\xfd\x07\xae\xcf\x9e\xef\xfb\xce\xf3w\x1e\xba\xe2\xe7\x0bV" + - "I+\xda\xfe\xbe\x03\x80\xdf\xdb\x96\xf7\xff\xac\xf1\xda\xb1" + - "?>\xfc\xd3\x83\xa0\xf4H\xfewN\xac\xed\xfa\xc4=" + - "\xf0\x1f\x00\xb8r(\xbf\x17\xd5\xeb\xf22\x80\xba)\xbf" + - "\x11\xd0\xff\xf9\x15\xfb\xde\xd9\xfe\xfb\xbbo\x03\xa5\x07S" + - "\xce\x9c\x0c\xb0\xb2\x91\x9fF\xf5\x96\xbc\x0c\xcc\x7f\xe0\xfa" + - "\xae\x7f\xc6\x07?\xbe\x1b\x94\xaf!@\x1b\xd2\xcfZ~" + - "\xa1\x04\xa8N\xe5K\x80\xfek\x97\x9dx\xfe\xd0\x8fn" + - "\xfd>\xf0\xaf\"Bx\xfeH\xfe\x7f\x10P}<`" + - "\xf8\xe0\xaf\xbe\x9e{\xfc\xb5\xf3~\x100\xf8\xc7_\xbf" + - "\xe6\xe9C?\xfa\x83\xf7`\x93$c\x0e`\xe5\x9by" + - "\x9bx\xdf\xca\xff'\xa0\x7f\xef\xbf\x9d\xdc\xd0\xb8\xfb\xe8" + - "1P\xbe\x1a\xdf\xf5\x13Y\x92 \xe7_\xf5\xef\xa77" + - "\xae\x7fz\xfc\xa1\xf0\x97P\x8eg\xe5\xa7\xe9\xe8\x8b2" + - "=\xf3\xf2\x0d\xc5;\xfa\xff\xe4\xae\x87\x80\xf7`F\x9f" + - "6\xd2x\xe5\xbb\xf24\xaa\xb8\x80>?\x93\xefB@" + - "\x7f\xfa\xea\x93\x9b\x7f\xff\xe7\xce\xa3\xc0\x97c\xce\xff\xc7" + - "\xdb\xdf\xdes\xc9#\xe3/\x05R1\x80\x95\x8f\xb7\x1f" + - "\xa3\xabO\xb6?\x01\xe8w\xfe\xdd\xa5\x1b\xeezg\xdd" + - "Stu\xc6\xa8\xa1\x10\xdb\x16\xf6\xa1\xdaXHv\xd5" + - "\x17\x12\xf7\xcf.\xdb\xfc\xe3\x1f?Y\x7f\xaaU\x10\x89" + - "\xb8\xdb\x17\xadEu\xe9\"\xe2\xee^D\xdc_\x1a\xc1" + - "_\xbc\xb0\"\xf7\xb7\x91^\x8c\x98^]\xf4^`\x92" + - "\x80\xe1\xfaO\x9f\xfd\x87\xa1\xf7\xdfx.\xeb\x80\x9b;" + - "$r\xc0\xe1\x0eR|\xe9o\x07:\xcd\xf7\x0f\xbc0" + - "\xd3\x8f\xe1M';\xd6\xa2\xfaz\x07=\xf7j\xc7\x13" + - "\x80\x1f?z\xeb\xa1\x91\xb7W\xbf\xc4{0\xd7\xaa\x88" + - "\xde\xb9\x17\xd5\x1b;\xe9s\xaa\xb3\x97l\x94X\xa5\x85" + - "=\xd0\xe4\x91\xc5;Q=\xb9\x98>\x9f[\x1c\xb0\xaf" + - "\xbd\xfe{\xf7\xb4\x9d\xfe\xdeK\xadf\x92\x037\x16l" + - "T\xdf,\xd0\xe7\xeb\x85\x87$@\xbf\xe7\xc9?\xfd\x9b" + - "\x81\xda\xa9\x9f\xb6\xc8M\x97\xab'\xcf\xfbP}\xe5<" + - "\xfaz\xf1\xbc\x1b\x00\xfd[\xbf>\xb5w\xc3\xc5\xd3o" + - "\xb6\xda4\x10\xfc+\xea4\xaaW\xab\xc4}\x95J\xdc" + - "\xd2i\xad\xfb\xa6\x7f\xfd\xe6/2Qt\\\xfd\x15B" + - "\xce\xdf\xb0\xf9\xfa\x9d\xed7\xbe\xfdv6\x8a\x8e\xa8\x81" + - "\xb5\x1fW\xc9\x98\xcf(\xf7\xa8'~\xf8\xd7\xef\xd0C" + - "r\xab5_U\xb7\xa0\xfa\x16=\xb4\xf2\x97j\xa0C" + - "\x12\xcds\xf9\xfa\xd4\xf9}\xa8\xbe{>\xc9u\xfa|" + - "\x92\xeb\xaa\xed\xfdb\xeb7\xae}\x0f\x94\x1e6#7" + - "/\xbe\xa0\x0f\xd5\xab.\xa0C+.\x90Q}\x8b>" + - "\xfd\xef\xd6\xb7\xbc\xf2\xc1\xe0\x0f\x7f\xd7zy\xa0\xd0+" + - "t\xe4Tp\xe4\xcd\x0b\x02\xf3\xaf\\\xf1\x17\xbf=\xfc" + - "\x97\x83\x1f\xcc\xba\xfd+\xdd\x03\xa8\xae\xe8&9\x96w" + - "\x7fK\xdd\xd6\x1d\\\xfe\x9d\xd5\x1b\xaf^\xf6\x93\x0f\xb3" + - "\x96\x18\xea\xfe\x90,q]7Yb\xfc\x1b\xbf\xf9\xd6" + - "\xc5\xdf\xfd\xa7\x0f[\xdc\x130Nu_\x8a\xea\xed\xc1" + - "\x8d\xb7\x10\xf3\xfbk~\xf0FO\xa1\xe7\xa3\xb9\x04}" + - "\xa4\x9b\xe2\xa4;\x88\x93\xee@\xd0k\x7fu\xf4\x86\xd2" + - "\xf7?\xfa\x98\xf4b-\xc8\xf3n\xcf\x16T?\xeb\xa1" + - "\x9b?\xe9\xa1\xf0_\xf7\xd8\xa9oN\x1c~\xf9\x93V" + - "#\x04\x0e9~\xe1\x01T\x9f\xbb\x90\xb8\x9f\xbd\x90\xf0" + - "c\xdf\xfbG\x86\xef\xda\xfa\xd8\xe7Y\xad\x1eX\xf2|" + - "\xe0\xdf%\xa4\xd5\xce\xc3\xfb\xdc\xe1\xfb\xee\xf4\xe7J\x96" + - "W\x97\x0c\xa0\xfa\xcb%t\xdb\xa9%O\xc0r\xdf\xf5" + - "LS\x18v3W\xfd\xc3\xf8\xb3zyUk\x9a\xcd" + - "\xbe\xa1I\xddqu\xb3>\x16\xd0Ke\xcb\xd0\xabS" + - "eD\xde\x81\x12\x80\xb2\xb4\x0f\x00Q\xf9\xd2\x16\x00\x94" + - "\x14e\x00\xa0\xa4\xd7M\xcb\x16~Mw\xaa\x96i\x0a" + - "`Uw\xff\x0e\xcd\xd0\xcc\xaaH\x1ej\x9b\xfd\xd0\xb0" + - "0\x0c\xeb\x1a\xcb6j\x1bm\xbd\xae\x9b\x83\x969\xae" + - "\xd7\x01\xca\x88\xc91y\xf6\xb1AC\x17\xa6[\x11\xf6" + - "\x1e\xbd*.\xf7\x1c\x11\x9e\xf3l\xcd\xd5-\xf3\xa2Q" + - "\xe1x\x86\xeb\x00\xf0\x1c\xcb\x01\xe4\x10@\xe9\xec\x03\xe0" + - "\x0b\x18\xf2.\x09Kv\xc0\x80\xc54\xf3\x00\xb1\x08\xe9" + - "\x9b\xf9\xd9o\x86\xb6\xa07\x85}\xb9g\xda\xa2\xae;" + - "\xae\xb0C\xf2E\xa5\xb2fk\x0d'\xfb\xe0Q\x00^" + - "d\xc8\x97H\xe8\xd7m\xad*\xca\xc2F\xdd\xaam\xd0" + - "L\xab\xc2D\x15\xdb@\xc2\xb6\xcc\xa3s8b\x8d\xa6" + - "\x1b\xa2\x16jwy\xb57\xf8?/\xb2\\\x87\xef\x07" + - "\x8fh[\x00\xf8v\x86\xdc\x90\xb0\x13?\xf7\xbb\xa8H" + - ")\xfa^\x00>\xc1\x90\xbb\x12vJ\x9f\xf9]\x81\xd7" + - "v/\x03\xe0\x06C>)a'\xfb\xd4\xef\xa2R\xa0" + - "x;\x01\xb8\xcb\x90\xdf$\xa1\xefxM\xb2\xa9\x03\xcc" + - "\xb2\xb1\x98\x86rd\x1dQ\xab\x93\xa5M(\x89*\x19" + - "\x1a\x8b1\xe2\x86\x0cr\xcd\x9a\xc0bZ\"\xa2c\xb6" + - "\xd8#lG\x94\xa1`[\x93SXL\x91\xb7\xc5\xea" + - "\x9d\xe7j\xf5\xd8\xd1\xc9\xa9\xf9\xcf\x07\xa1Yu/*" + - "\xf7\xcer\x16\xd9\xb1\x83!\xff\xb2\x84~\x93~\x15\xae" + - "\x00f;XLKo\x8b\xb4s\x84\xf3 \xfdw0" + - "|\xa5\x1c\xddb;A8\xf3%\xc9c\xcf\xd2c\xcf" + - "0\xe4/H\xa8 \x86>;i\x03\xf0\x13\x0c\xf9\xcb" + - "\x12\xa2\x14z\xec\xc5c\x00\xfce\x86\xfc\x0d\x09\x15&" + - "\x85\x0e{\xfdR\x00\xfe/\x0c\xf9\xaf%Tr\xac\x8b" + - "\xda\x0c\xe54\x05\xdb\xaf\x19\xf2\x8f$T\xdar]\xd8" + - "\x06\xa0|@\x9c\xbfa\xc8\xff[B%\xdf\xd6\x85y" + - "\x00\xe5\xbf\xae\x04\xe0\xbfc\xc8?\x95\xd0\xb7\xc2\xa4#" + - "M]\xec\x04\x09;\x01\xfd\xaaay\xb5qC\x83^" + - "[\xd4FV't\xd3k\x94m\xb1GG\xcbs\xfa" + - "]W4\xe4\xa6\xeb`\x1e$\xcc\x03\x16\\\xad\xee\xe0" + - "b\xc02C,\xa6\xd5\x0f\x90\x88\xc9\x9dh\x8b\xdaf" + - "a;:\xb3L\xec\x00\x09;\x00\x0b\xa6\xd6\x10\xf1?" + - "z\xeb\xb6\xe55\xe3\x7f\xcdg\xeb\xd1(\xaa(\xa6\xa2" + - "\x04\xb1l]\xae\xeb&\xef`\xb9%\xbe\x1f\x19v\x88" + - "\xac\xb0\x8a!_'\xe1R\xfc\x9c\xc8d\xdb\x91Q\x00" + - ">\xcc\x90\x8fI\xb8T\xfa\x8c\xc8d]N\xbe)3" + - "\xe4[%,L\xb8n\x13\x8bi\xd5\x8c\x02\xe0\x06\xb1" + - "\xc3\xb1\xaa\xbb\x04 AH\x02\xe1\xd1\xaf\x13\x11\xa4\x01" + - "3jXL\xfb\xd0\x96\xe8asDO\x108%w" + - "\xc8\xb6-;@\xdb$d\x86\xaeL\x95\x88#fd" + - "K\xaa\x81\"\xad\x0a\xd5\xe2;R\xf9{\xab\x9a\xe7$" + - "\x96\xf5m\xe1\xdaS\xfd\xe3.0a'\xd8\xe3LX" + - "\x9eQ\x1b\x15 \xbb\xf6\x14\"H\x88\xf3#\xd2jk" + - "8c\xf20\xb43r\x92L\xab\x19\xf2r*\xe7z" + - "\xa2\xadc\xc8\xaf%9#\xf3o\"\xf3\x8f1\xe4M" + - "\x09}\x83r\xda\x1c\xb6\x809n\"nH,[A" + - "l\xca \xa1\x0c\xe8{M\xc7\xb5\x85\xd6\x00L\x82\x8d" + - "\xf8\x17\x9f\x03t\xb7@HY+\x04X0\xb7\x0eI" + - "z\xae_\x9bU\"\xca\xcfM\x03\xa9\xb1\xe7\xce\xa5\x09" + - "\xcbq)\xbc\x01 Vl\xbf\xd5$\xec$dIZ" + - "\xca\x96\xd88\xf7\x8a\x17V\x9f\x19\xf5\xeeX\xa6\xfcT" + - "\xa3\xd3\x18\x1c\x1f\xb4Ly\\\xafc1\xed\xc1Z\x04" + - "\x98\xc3\xef\xfd\x9e;!LW\xaf\x06\x0f\xce\xf2\xfb\xb2" + - "4>\x13\x9b\x8d\\\x991dl\xb3\xf5;RC\xca" + - "\xbb\xc4T\x92\xf8\xa2\xa1\xe9F\xe2\xfd\xc8\x9a\xfd \x7f" + - ";\xe5\x99\xb7e\x89jSX\x99J\xa1yH\xc8\xae" + - "D\xc8\x1b\xa7\x01\xf8M\x0c\xf9\x1d\x19!o\xbf\x07\x80" + - "\xdf\xc1\x90\xdf\x97\x11\xf209\xf6\x10C~?\x01/" + - "\x0b\xa1\xe1\x08Y\xf4~\x86\xfca\x091\x17\xe2\xeeq" + - "\xc2\xdd\x87\x19\xf2g\xa4\x00 \x87\xfb\x07-\x13#!" + - "\x1c\x80\x18\x1e\xfd\x09\xa1\xd9\xee\x0e\xa1\xa1;b\xba\xc2" + - "\xde\xa3\xa1\x11\xe7\xe0~Wo\x08\xcbs\x93\x9clh" + - "\x93A\xdd\xc7\xdapxJ\xd6\\\x07\xdbA\xc2vJ" + - "\x01G\xd8\x83\xb6\xa8!yC3\xca\x1as'\xce\xc6" + - "@3\xf1\xb20\x87y\xa8k\xd8\xc7\x90\xdfF\xb9\x8b" + - "\x99!R\xb9\xa5\x0f\xa4 uI\xe7\xc6@\xda]\x04" + - "U\x89j\xcd\xee{\xd26\"\xa8JTkn<\x9a" + - "\x1a<\x12m\xd8\x82R\x98\x12\xb1\xcc\xa5\xd0\xd5\xfb\x09" + - "\x9et\x91\xea\x19\x15k\x1d-s,0\x10\xa6\x16\xaa" + - "Z\x8d\xa6-\x1c\x07u\xcb\xe4\x9ef\xe8\xcc\x9dJ\x0e" + - "\xcek\x03\xca\xfd0g66{\x03'\x91\x11\xae\x88" + - "\x8d\xa0\xf6\xe3Z\x80\xca*dXY\x87i\x98\xa8#" + - "8\x00PYM\xf42\xa6\x91\xa2\xae\xc7\x1e\x80\xca0" + - "\xd1\xc7PB\x0ccE\xe5\xf8(@e\x8c\xc8\xdb1" + - "\xad\xd3\xea\xb6\xe0\xfa\xadD\x9f\xc0\xb4T\xab\x02/\x05" + - "\xa8l'\xfa>\xa2\xe7\xa5\xc0\x82\xea\x14\xee\x04\xa8L" + - "\x12\xfd \xd1\xe5\xb6.\xea\xfa\xd5\x9b\xd1\x06\xa8\xdcD" + - "\xf4;\x88\xbe\xe0\xcb]\xb8\x00@\xbd=\xa0\xdfF\xf4" + - "{\x89\xde\xde\xdd\x85\xed\x00\xea\xddx\x00\xa0r\x88\xe8" + - "\xf7\x13}!v\xe1B\x00\xf5\x08\x1e\x05\xa8\xdcO\xf4" + - "\x87\x89\xbe(\xdf\x85\x8b\x00\xd4\xe3\x81<\x0f\x12\xfd1" + - "L\x00d\xa4\x96\xc51\x0a'=-\xe3\xccr\x920" + - "\x14\xd1\xfc\x80!\xc8\x96\xad\x02\x0d\x10XH\xf77\x80" + - "X\x00\xf4\x9b\x96el\x98\x89\x8fg\xea$\xa2\xb0\x80" + - "\x82e\x8e\xd4\x92\xfc\x0a\x83h\x9d\x05\xbdU\xcd\x18I" + - "\x1b\x08\xdd\xe9\xf7\\\xcbkBoMsE-\xa9p" + - "\xb6g\xae\xb1\xad\xc6\x18\x0a\xbb\xa1\x9b\x9a\x01\xc9/\xf3" + - "\xc5V\xc1\xf3\xf4\xda\xacd\x93Z\x03\xad\xb7\xd97\xa6" + - "\x05\xd9\xb5 \xc9\xaeK\xa8\x0d\xb9\x88!\xbf\"\x03>" + - "\xcb\x09!\xbf\xc6\x90\xff\x91\xd4\xd2\x06\xed\xd1\x0cO\x9c" + - "M\x1b\xb4\xa9\xa5\x14\x84-q\x88\xcf\x99\xd7\x07\xd2\xd7" + - "\x93\xc7\xa9\xe3\xbc\x8c!\x1f\x96p\xbf\xe3U\xab\xa4t" + - "l\x85\xf1h\xf0\x80^\xba;\xe3\x8fd'\x10\xf9\xe3" + - "l\xcbn]\xb8\xe1\xd7\x889nQ\xbd\x92\xb5\x86\xf3" + - "\x7f<=*\x9c\x02\xf5\xfdg\x1c\xef\x92)\xff\xcc\xf5" + - "mxl\xac\x9c\xce\xa0,\x04\xc7,.\x8cfq!" + - "\x85\x85\x9d\xd9\xf4\x8f\xdb0\x95\x07yX&\xfaVL" + - "\x9bw\xf5:<6#\xffs\xfd!.\x88\xe0\xfa\x1a" + - "\xd1\x9b\x01.`\x88\x0b\x8d\xe0~\x83\xe8\x93Y\\\xf0" + - "pz&.\xb0\x18\x17(\x9f\x0f\x12\xfdP\x80\x0b\xb9" + - "\x10\x17\xee\xc4\xa7g\xe4\x7f{[\x88\x0bG\xf0\xf9\x19" + - "\xf9\xbf0\x1f\xe2\xc2\xf1\x80\xffa\xa2?\x13\xe0\xc2@" + - "\x88\x0bO\x058\xf2$\xd1O\x10.x\xb6Qqm" + - "\xdd\x04\xac\xa7\xc1Zm~[\x88f?\x14\x0c}\x8f" + - "H0\xbb\xa6k\xc6jO3\xa0\xb7\xe2j\xd5]i" + - "\xafi8\xc3\x9aYspB\xdb%\x08\xe9\xe5l-" + - "t\x0dg\xb3\xb0\xf5q\xc0\xb4;Mz\x83B\xd9\xb2" + - "Z[\x86\xa0\xc9\x11v\x08*\xc9o\x0dmr\xa4f" + - "\x88A\x8c;\x04f\xa6\x95F\xa7_,\xd3\xc4\xb0l" + - "\x8f\xe9\xbd3\xebq3\xeaw\xe3\xba>Vj)\xd8" + - "b\xb2)\xaa\xee\xa0\x85\xa6\xab\x9b\x9e\x98uAu\xc2" + - "3w\x89\xda\x10\x9aU\xab\xa6\x9bu\x98\xd5h\xb3/" + - "\x1a\xfd3\x8dL\x90\xcd\x98\xd98+\x97PY\xc6\xa8" + - ",+}\xe9\x08[\xaa\x06\xa7J\xb6\xd0\x9c\x14\xa1\xe7" + - "{-\xda\xf4\x84IF\xaf\x15Y\x1b@\xb2\xbf\xc5x" + - "\xa1\xa6\xec\xde\x0b\x92\xa2\xcb\x98\xee!1^;*\xdb" + - "l\x90\x94M2J\xc9\xda\x1c\xe3\x95\xb722\x0d\x92" + - "2$#Kv\xdd\x18\xaf\xb3\x94\xab\x07@R\x96\xcb" + - "~\xdc\x9aC)\x14g\x15\xfaq\xe2Co\x90\xfa\xab" + - "\xd0\x8f\x97\x00\x18\xb7\xf0\x00\xabp\x7fT\x16Vav" + - "s\xc4\xbe\xa8\x8f\x9e\xbb=$\x8c\x9cd\xc8\x0f\xa6\x18" + - "y3\xb5\x8c\x07\x19\xf2C\x99\xd9\xe5\xceG\xb3\xdda" + - "4\x96\x1f9\x00\xc0\xef\x0b;\xc1d,\x7f\xeaX4" + - "\xe9\xffLJ\xebe\x1cv\xf1\xb2\x05-;\x1e\xa6\xe6" + - "\xd9\xb9D\xc1\x19un\xad\x9b\x17\xbffM\x04\x9d\x1d" + - "\x86W9\x90\"vv\x1d\xb38\xb3\x8e\xc1x\x8c\x93" + - "g\x00|v9\xb3x~\xcc\x9c1\x94\x04\x15'\x17" + - "DM\xbc\xd4\xc7\xf8\xcf+\x8aB\xde\xef\x94\xfdxp" + - "\xc1\xb8\\\x91\xf3\xb2.;\xc7\xe9mT\xf4:gS" + - "\x09\xe2\x15\xee\x99\x87\xf0\xf0\x9d\x02\x05[\xa8Pr\xef" + - "\xce\xcc\x8a\xc8\xb0\xa29\xa8\xb0!S\xb5\xe7\xb3U(" + - "p\xdc\x80\x16\xe8pK\xf8-K\xc3/i\x10n^" + - "\x96\xf6\xe4\xc9tr\xcb\xda((\x1fL\x1aN\xe5\x01" + - "\x0a\xd4\x07\x19\xf2\xc72\xe1\xf7\xc8\xdat:\x91\x85m" + - "\xc7r\xca\x9e\x9d\xc2\xa6a\xd5\xd7\xe9\xa6p\xa8\x05k" + - "\x19\xa9\x9b\xc2nh\xa60\xd1%0\xf2lB\xd4\x99" + - "\xc85\xb2:\xd3\xb9\xcd\xa7~%\x0a\xf60\xd6\xa3\xf2" + - "\x9a\x99\x1f\x8fev\x19\xb1\xf2\xfc\xf9hG\xb0=\xa3" + - "\xfc6\x9a\x1f\xb72\xe4\x13\x12\xfa\x9a\xe7Z\x9b\x9a5" + - "\x0d]\xb1\xc6\x16\xbb=!\x9b\xd5\xa9t\x8e\xa2\x89\xa2" + - "\xeal\xc2&\xf5~klQ\xda\xed\x89,C\xbc\x95" + - "\x05Y\xb7j\xb3\xd6\xb1s4[\xd7\x88\x1d\x15\xab\xba" + - "K\xb83\xb6\xd5!\\\xc6\xaah\xa3\xe9J6\xd6D" + - "\x1f\xcd\x8cL1\x8c\xec\xa6\x80j2\xe4\xfb202" + - "5\x9d:|\xee\xea\xfa\xffS\x10\xe7Qr\xce\x95\xe9" + - "hI\x9cU\xa2\xa5\x7f\x828s\xcb\x15\x8d\xebQ\xc7" + - "z6\x0b\x85\x19\x1b\xaf( \xf8\xcet\x09\x13\x04y" + - "1\xfd\x8bf$\x83\x13\xb5\x8e\xc0\xc6\xad9\xba\xc2p" + - "\xb8\x19\xb4 \x82\xd6\xd6\xb5\xc8\xff\x06\x00\x00\xff\xffX" + - "t1W" +const schema_db8274f9144abc7e = "x\xda\xacY}l]\xe5y\x7f\x9e\xf3^\xfb\xd8\x89" + + "o\xae\x0f\xe7\"b\x13\xcbU\x04jCI\x06\xf1\xb2" + + "\x11o\xeb\x8d\xed$\xb5\xdd|\xdc\xe3\x8f\x00!\x91r" + + "r\xef\xeb\xeb\xe3\x9c{\xce\xcd\xf9\x08v\x946\x90\x85" + + "\x01\x1e)\x09m&\x92\x86\x0a\xb2f|(]\x09\x0d" + + "\xda\xca\xa0*\xd3\x18d\xad\x04h0\xc1\xc6\xfeX!" + + "\x9a@\x8b\x18\xb4\x12\xa2*\x9c\xe99\xdf\xbe\xbeu\xc2" + + "\xd4\x7f\x92\xab\xe7<\xef\xfb>\x9f\xbf\xe7\xc37e[" + + "\xd7\x0977\x99Y\x00\xe5TS\xb3\xf7\xa7\xd5WO" + + "\xff\xd1\xf1\x9f\x1f\x06\xa9S\xf0\xbe\xf5\xfcp\xfeS\xe7" + + "\xd0\x7f\x00`\xcf#\xcd\xfbQ>\xd7,\x02\xc8?l" + + "\xde\x0a\xe8\xfd\xebM\x07\xde\xdb\xf5\xabc\xf7\x81\xd4\x89" + + "\x09gF\x04\xe8y\xa9y\x16\xe5\xffl\x16\x81y\x8f" + + "\xdc\x91\xff\x17|\xf4\x93c }\x05\x01\x9a\x90>\xff" + + "\xa4y\x91\x00(\xbf\xde\\\x00\xf4^\xbd\xf1\xf9\xe7\x8e" + + "\xfe\xf8\xde\xef\x81\xf2eD\x08\xce\x7f\xd4\xfc\x1b\x04\x94" + + "\x9bDb\xf8\xe8\x07_\xcd\xfc\xf0\xd5\xab\xbe\xef3x" + + "g^\xbf\xf5\x99\xa3?\xfe\xd2\x070.\x88\x98\x01\xe8" + + "Y)Z\xc4\xbbV\xfco@\xef\xbbo\xbd\xb0\xa5z" + + "\xec\xe4i\x90\xbe\x1c\xdd\xd5\xd1\"\x08\x90\xf1\xd6\xfc\xfb" + + "\xc5\xad\x9b\x9f\x99x<\xf8\x12\xc8\x91my\x86\x8ev" + + "\xb5\xd03\xaf\xdc\xd9\xfe@\xdf\x1f?\xf88(\x9d\x98" + + "\xd2\xa7\xa9\x898\xfbZfQ\xbe\xbd\x85~\x8e\xb7\xdc" + + "\x8a\x80\xde\xec\xda\x17\xb6\xfd\xea/\xec\xa7@Y\x89\x19" + + "\xef\x9f\xee\x7fw\xdf\x8a''^\xf6\xa5b\x00=\x17" + + "ZO\xd3\xd5o\xb7\xfe\x08\xd0\xcb\xfe\xc3\x0d[\x1e|" + + "o\xd39\xba:e\xd4@\x88\x99E\xbd(\xdf\xbf\x88" + + "\xecz\xcf\"\xe2~\xed\xc6m?\xfd\xe9\xd3\x95s\xf5" + + "\x82\x08\xc4}\xfd\xe2a\x94\xd7.&\xee5\x8b\x89\xfb" + + "\xea!|\xe7g7g\xfe.\xd4\x8b\x11\xd3\xfb\x8b?" + + "\xa0\xc7?\xf3\x19\xee\xf8\xed\xb3\xff\xb8\xe1\xc37~\x92" + + "v\xc0#m\x029\xe0\\\x1b)\xdeu\xa9?k|" + + "x\xe8gs\xfd\x18\xdc\xf4v\xdb0\xca\x97\xda\xe8\xb9" + + "\xf7\xdb\xe8\xb6\xbfr\xdfz\xc3\x1c\x1e~\xa9^8\xff" + + "\xda{\xb2\x02\xca\xc7\xb2\xc4}$[\x00\xfc\xe4\xa9{" + + "\x8f\x0e\xbd\xbb\xfee\xa5\x133\xf5\xbc\xc7\xb2\xfbQ>" + + "C\xbc=\x8fe\xbb\xc9\xa2\xb1\x0d\xeb\xd8}\xbd__" + + "2\x85\xf2\xc5%\xf4\xf3\xbf\x96\xf8\xec\xc3w|\xe7\xa1" + + "\xa6\x8b\xdfy\xb9\xde\xa8\xa2o\x81\x9c\x85\xf2g9\xfa" + + "\xf9i\xeeq\x01\xd0\xfb\xcd\xd9?\xe7\x17W\xbdr\x01" + + "\x94/aJ\x8dq\x14Q\x00\xe8\xb9x\xd5j2\xd9" + + "\xa5\xab\xee\x04\xf4:\x9f\xfe\x93\xbf\xed/\xbf\xfd\xf3:" + + "\x8b\x90 \xf2f\xf9c\xf9v\x99~\x8d\xcb\xc4{\xef" + + "Wg\xf6o\xb9~\xf6\xcd\x86\x069'\xcf\xa2|\xc1" + + "\xe7~\xc9\xe7\x16.\xaa\x1dw\xfd\xdb\xd7\xdeI\xc5\xe7" + + "\x9a\xfc/\x112\xde\x96mwL\xb5~\xf3\xddw\xd3" + + "\xf1\xb9\"\xef\xfb\xf1\xcf\xf2\xe4\xa6\xf3\xd2C\xf2\xf3\x8f" + + "\xfd\xcd{\xf4\x90X\xef\xa7\x9d\xf9\xed(\xef\xcd\xd3\xcf" + + "j\xde\xd77\xce\x93FQ\xa4]\xd3\x8b\xf2\xcc5$" + + "\x97{\x0d\xc9\xb5fW\x1f\xdfq\xcbm\x1f\x80\xd4\xc9" + + "\xe6d\xfd\xb3\xc4\xf9\x12q\xf6\xbcx\x8d\x88\xf2\xde\xa5" + + "\"\x80\xf7\xed\xca\xf6\x0b\x1f\x0d<\xf6\xbf\xf5\x97\xfb\x0a" + + "\xdd\xbe\xb4\x17e\x8d\xf8z\xf8R\xdfU=7\xff\xe5" + + "\xa5\xe3\x7f=\xf0\xd1\xbc\xdb\xcfu\xf4\xa3\xfcb\x07\xc9" + + "\xf1B\xc7\xd7\xe5K\x1d\xfe\xe5\xdfZ\xbfu\xed\xf2\x17" + + "?N[\xe2\xcd\x8e\x8f\xc9\x12\xefw\x90%&n\xf9" + + "\x9f\xaf_\xff\xed\x7f\xfe\xb8\xce=>ck\xe7\x0d(" + + "wt\xd2\x8dWw\x16\x00?\xdc\xf8\xfd7:s\x9d" + + "\xbfn$\xe8\xda\xce)\x947\x13o\xcfP\xa7/\xe8" + + "m\xbf\x03\xb6'\x99\x07" + + "\x88\xed\x90\xbc\xd9<\xff\xcd\xc0\x16\xf4&\xb7V\xb9\x86" + + "\xc5+\x9a\xedp+ _W(\xaa\x96Z\xb5\xd3\x0f" + + "\x9e\x04P\xda\x19*\xcb\x04\xf4*\x96Z\xe2En\xa1" + + "f\x96\xb7\xa8\x869\xcax\x09\x9b@\xc0\xa6\xd4\xa3\x0d" + + "\x1c\xb1Q\xd5t^\x0e\xb4[U\xea\xf6\xffW\xdaY" + + "\xa6\xcd\xf3\xfcG\xd4\xed\x00\xca.\x86\x8a.`\x16?" + + "\xf7\xf2T\xfe$m?\x802\xc9Pq\x04\xcc\x0a\x9f" + + "yy\xdfk{\x97\x03(:CeZ\xc0,\xfb\xad" + + "\x97\xa7\"#\xb9S\x00\x8a\xc3P\xb9K@\xcfvk" + + "dS\x1b\x98ia{\x12\xca\xa1ux\xb9B\x966" + + "\xa0\xc0Kdhl\x8f\xd09`\x10\xcb\xe6$\xb6'" + + "\xc5'\xb5\xf0y?" + + "4K\xceu\xc5\xeey\xce\";\xb61T\x96\x0a\xe8" + + "\xd5\xe8+w80\xcb\xc6\xf6\xa4\xa8\xd7I\xdb \x9c" + + "\x07\xe8\xdf\x81\xe0\x95bx\x8be\xfb\xe1\xac,\x8d\x1f" + + ";A\x8f=\xccP\xf9\x81\x80\x12b\xe0\xb3\xc7,\x00" + + "\xe5Q\x86\xcaY\x01Q\x08<\xf6\xe4i\x00\xe5,C" + + "\xe5\xef\x05\x94\x98\x108\xec\xd9\x1b\x00\x94\xa7\x19*\xbf" + + "\x10P\xca\xb0<50\xd2\x05\x0a\xb6_0T\xde\x12" + + "Pj\xca\xe4\xb1\x09@zs5\x80\xf2\x1aC\xe5\x1d" + + "\x01=3\xc8/R\xca\xc1,\x08\x98\x05\xf4J\xba\xe9" + + "\x96't\x15\xba-^\x1eZ\x1f\xd3\x0d\xb7Z\xb4\xf8" + + ">\x0dM\xd7\xees\x1c^\x15k\x8e\x8d\xcd `3" + + "`\xceQ+6.\x01,2\xc4\xf6\xa4\xd0\x01\x121" + + "\xbe\x13-^\xde\xc6-[c\xa6\x81m `\x1b`" + + "\xb7]2k\x1c\xdb\x93\xdayy\x9b\x8e\x84\xd1C\xb1" + + "\x13&\x82iibE3\x946\x96Y\xe6y\xa1\x01" + + "7\x90]\xd61T6\x09\xd8\x85\x9f\x13\x99l84" + + "\x02\xa0\x0c2T\xc6\x04\xec\x12>#2YQ!\x1f" + + "\x14\x19*;\x04\xccM:N\x0d\xdb\x93\xea\x18\x0au" + + "'\xdfm\x9b\xa5=\x1c\x90\xa0\"\x86\xea\xf0\xebd\x08" + + "]\xc0\xf42\xb6'\x9dl\x9dF\xacA\x94\xf8\x01R" + + "p6X\x96i\xf9\xa8\x1a\x87\xc6\x86\xd5\x89\x12Qd" + + "\x0cmO4\x90\x84u\x81Z\xca\xeeD\xfe\xee\x92\xea" + + "\xda<\xb2\xb1gq\xc7\x9a\xe9\x9bp\x80q+\xc6\x18" + + "{\xd2t\xf5\xf2\x08\x07\xd1\xb1f\x10A@\\\x18y" + + "\xd6\x9b\x83)\x93\x07!\x9c\x92\x93dZ\xcfP)&" + + "rn&\xda&\x86\xcam$gh\xfeq2\xff\x18" + + "C\xa5&\xa0\xa7S\xee\x1a\x83&0\xdb\x89\xc5\x0d\x88" + + "E\xd3\x0fL\x11\x04\x14\x01=\xb7f;\x16W\xab\x80" + + "q\xa4\x11\xff\x92/\x00\xd1uPQTs~\xce7" + + "\xd6!N\xc3\xcd\xc3i%\xc2<\x1c\xefO\x8c\xdd8" + + "\x91&M\xdb1\xd4*\x07\x80H\xb1\x83f\x8d0\x92" + + "\x10$n3\xebb\xe3\x8bW\xb6\xa0\xca\xcc\xa9k\xa7" + + "Se\xa6\x14\x9eF\xff\xf8\x80i\x88\x13Z\x05\xdb\x93" + + "^\xabN\x80\x06~\xefs\x9dIn8Z\xc9\x7fp" + + "\x9e\xdf\x97'\xf1\x19\xdblhu\xca\x90\x91\xcd6\xef" + + "N\x0c)\xee\xe131\x04\xf0\xaa\xaa\xe9\xb1\xf7Ck" + + "\xf6\x81\xf8\x8d\x84g\xa1\xe4\x19%\x04\xf1\xa5\xf2m\x80" + + "\xa9\xae\\\xca\xae\x06\xa1{\x9f\xaa\xbb|\xc1\xe6&\xac" + + "bA\x0d+\x04\x06\xa6\x0b\xf3\xb1\x9a\xdf\x9c\x05P\xee" + + "b\xa8<\x90R\xf3\xfe\x87\x00\x94\x07\x18*\x0f\xa7\xd4" + + "\xff\x13D?\xef\x03R" + + "\x7f\x00H\xe7|\x00{\x9a\xe8\xcf\x13 \xb9\x96>\xea" + + "X\x9a\x01XI\x82\xb5T\xfb\x06\xe7\xb5>\xc8\xe9\xda" + + ">\x1e\x17\x8b\xb2\xa6\xea\xeb]U\x87\xeeQG-\xed" + + "I\xdad\xdd\x1eT\x8d\xb2\x8d\x93\xea\x1eN%FL" + + "\x17aG\xb7\xb7qK\x9b\x00L\x1a\xeb\xb8\xad\xc9\x15" + + "M\xb3\xbe\xdb\xf1\xfb3n\x05h\x16\x7f\xab\xaa\xd3C" + + "e\x9d\x0f`\xd4\x9a0#)q\x1a}1\x0d\x03\x83" + + "~aL\xeb\x9e\xdb\x08\xd4\xc2V=j(\xc6\x0au" + + "\x9d\x02\x9f\xae\xf1\x923`\xa2\xe1h\x86\xcb\xe7]P" + + "\x9at\x8d=\xbc\xbc\x01\x8d\x92Y\xd6\x8c\x0a\xcc\x9b\x11" + + "\xd8\xef\xdaN\xa4:\xa8\x96\xb0%\x8b\xd7\xed\xd2\x0a\xea" + + "\x070\xec\x07\xa4\xded\xca.\x94\xfcS\x05\x8b\xabv" + + "R\x1a\x16z-\\F\x05IF\xaf\xb5\xb3&\x80x" + + "y\x8d\xd1\xceO\xda\xbb\x1f\x04I\x131Y\x95b\xb4" + + "\x19\x95vZ H\xe3\"\x0a\xf1\xdf\x0c0\xda\xf7K" + + "C\xb3 H\x1bDd\xf1\xa2\x1f\xa3\x8d\x9b\xb4\xb6\x1f" + + "\x04i\xa5\xe8ES\x05\x14\x02q\xd6\xa1\x17%>t" + + "\xfb\xa9\xbf\x0e\xbdhO\x81\xd1\xf4\x01\xb0\x0e\x0f\x86\xf5" + + "h\x1d\xa6\x97[\xecw\x8d\x00\x8d\xfbR\xc2\xc8i\x86" + + "\xca\xe1\x04#\xef\xa6^\xf50C\xe5hj\xec:\xf2" + + "T\xba-\x0d7\x07'\x0e\x85{\x87\xf3\xa9\xcd\xc19" + + "\xeaU\xcf3T^\x13\x92B\x1d\x85]\xb4\x0fB\xd3" + + "\x8a\xe6\xc0\x05\xd6Bap\x86-c\xfdr\xc8+\x9b" + + "\x93~K\x89\xc1U6$\x88\x9d\xde\x18-Im\x8c" + + "0\x9a@\xc59\x00\x9f\xde\x1f-Y\x183\xe7\xccS" + + "\xe1\xd8@Q\x13\xfdE\x03\xa3\xbf-I\x12y?+" + + "z\xd1\xcc\x85Q\xb9\"\xe7\xa5]\xf6\x05\x07\xcf\x11\xde" + + "m_I%\x88\xb6\xcc\x97\xdf\x1f\x04\xef\xe4(\xd8\xe2" + + "9(\xb8w*\xb5\xc5\xd2\xcdp\x84\xcbmIU\xed" + + "\x85l\x15\x08\x1cu\xbe9:\\\x17~\xcb\x93\xf0\x8b" + + "\x1b\x84\xbb\x97'\xc3@<\x16\xdd3\x1c\x06\xe5\xa3q" + + "\xa7+=2\x9bl\xb8\xe2\xf0{r8\x19\x8bDn" + + "Y\x91\x9c\xa2k%\xb0\xa9\x9b\x95M\x9a\xc1m\xea\xfd" + + "\xea\xb6\x015nUU\x83\x1b\xe8\x10\x18\xb9\x16!\xea" + + "\\\xe4\x1aZ\x9fj\x19\x17R\x7f4\x0c\xf6 \xd6\xc3" + + "\xf2\x9a\x1a}O\xa7\xd60\x91\xf2\xcas\xe1zcW" + + "J\xf9\x9d4\xfa\xee`\xa8L\x0a\xe8\xa9\xaec\x8e\xd7" + + "\xca*:|\xa3\xc5\xf7\xba\\4J3\xc9\x00G\xa3" + + "L\xc9\x1e\xc7\x1a5\x9d\x1b-^\xd8\xeb\xf24C\xb4" + + "8\x06Q3\xcb\xf36\xc6\x0d\x9a\xad[\xf9\xeeQ\xb3" + + "\xb4\x87;s\x16\xea\x01\\F\xaa\xa8#\xc9\xd68\xd2" + + "D\x1bI\xcdj\x11\x8c\xec\xa5\x80\xaa1T\x0e\xa4`" + + "df6qx\xe3\xea\xfa\xfb)\x88\x0b(\xd9p\xab" + + ";R\xe0W\x94h\xc9_I.\xdfr\x85{\x82\xb0" + + "c\xbd\x92]\xc8\x9ce]\x18\x10\xcaT\xb2?\xf2\x83" + + "\xbc=\xf9sn(\x83\x1d\xb6\x8e\xc0&\xcc\x06]a" + + "0U\x0d\x98\x10Bk\xfdF\xe7\xff\x02\x00\x00\xff\xff" + + "\xd4\xcdz3" func init() { schemas.Register(schema_db8274f9144abc7e, @@ -3810,9 +3944,11 @@ func init() { 0xb70431c0dc014915, 0xb9d4ef45c2b5fc5b, 0xc082ef6e0d42ed1d, + 0xc54a4a6fd4d87596, 0xc744e349009087aa, 0xc766a92976e389c4, 0xc793e50592935b4a, + 0xc9c82ee56583acfa, 0xcbd96442ae3bb01a, 0xd58a254e7a792b87, 0xdc3ed6801961e502,