diff --git a/backend/protos/xyz/block/ftl/v1/language/language.pb.go b/backend/protos/xyz/block/ftl/v1/language/language.pb.go index 8780609642..15f26b4c8e 100644 --- a/backend/protos/xyz/block/ftl/v1/language/language.pb.go +++ b/backend/protos/xyz/block/ftl/v1/language/language.pb.go @@ -130,18 +130,23 @@ type ModuleConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // name of the module + // Name of the module Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // absolute path to the module's directory - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - // absolute path - DeployDir string `protobuf:"bytes,3,opt,name=deployDir,proto3" json:"deployDir,omitempty"` - Build *string `protobuf:"bytes,4,opt,name=build,proto3,oneof" json:"build,omitempty"` - GeneratedSchemaDir *string `protobuf:"bytes,5,opt,name=generated_schema_dir,json=generatedSchemaDir,proto3,oneof" json:"generated_schema_dir,omitempty"` - Watch []string `protobuf:"bytes,6,rep,name=watch,proto3" json:"watch,omitempty"` + // Absolute path to the module's directory + Dir string `protobuf:"bytes,2,opt,name=dir,proto3" json:"dir,omitempty"` + // The language of the module + Language string `protobuf:"bytes,3,opt,name=language,proto3" json:"language,omitempty"` + // Absolute path to the directory containing all of this module's build artifacts for deployments + DeployDir string `protobuf:"bytes,4,opt,name=deploy_dir,json=deployDir,proto3" json:"deploy_dir,omitempty"` + // Build is the command to build the module. + Build *string `protobuf:"bytes,5,opt,name=build,proto3,oneof" json:"build,omitempty"` + // The directory to generate protobuf schema files into. These can be picked up by language specific build tools + GeneratedSchemaDir *string `protobuf:"bytes,6,opt,name=generated_schema_dir,json=generatedSchemaDir,proto3,oneof" json:"generated_schema_dir,omitempty"` + // Patterns to watch for file changes + Watch []string `protobuf:"bytes,7,rep,name=watch,proto3" json:"watch,omitempty"` // LanguageConfig contains any metadata specific to a specific language. // These are stored in the ftl.toml file under the same key as the language (eg: "go", "java") - LanguageConfig *structpb.Struct `protobuf:"bytes,7,opt,name=language_config,json=languageConfig,proto3" json:"language_config,omitempty"` + LanguageConfig *structpb.Struct `protobuf:"bytes,8,opt,name=language_config,json=languageConfig,proto3" json:"language_config,omitempty"` } func (x *ModuleConfig) Reset() { @@ -181,9 +186,16 @@ func (x *ModuleConfig) GetName() string { return "" } -func (x *ModuleConfig) GetPath() string { +func (x *ModuleConfig) GetDir() string { if x != nil { - return x.Path + return x.Dir + } + return "" +} + +func (x *ModuleConfig) GetLanguage() string { + if x != nil { + return x.Language } return "" } @@ -229,7 +241,7 @@ type ProjectConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Dir string `protobuf:"bytes,1,opt,name=dir,proto3" json:"dir,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` NoGit bool `protobuf:"varint,3,opt,name=no_git,json=noGit,proto3" json:"no_git,omitempty"` Hermit bool `protobuf:"varint,4,opt,name=hermit,proto3" json:"hermit,omitempty"` @@ -265,9 +277,9 @@ func (*ProjectConfig) Descriptor() ([]byte, []int) { return file_xyz_block_ftl_v1_language_language_proto_rawDescGZIP(), []int{1} } -func (x *ProjectConfig) GetPath() string { +func (x *ProjectConfig) GetDir() string { if x != nil { - return x.Path + return x.Dir } return "" } @@ -390,9 +402,9 @@ type CreateModuleRequest struct { unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // The root path for the module, which does not yet exist. + // The root directory for the module, which does not yet exist. // The plugin should create the directory. - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` + Dir string `protobuf:"bytes,2,opt,name=dir,proto3" json:"dir,omitempty"` // The project configuration ProjectConfig *ProjectConfig `protobuf:"bytes,3,opt,name=project_config,json=projectConfig,proto3" json:"project_config,omitempty"` // Flags contains any values set for those configured in the GetCreateModuleFlags call @@ -436,9 +448,9 @@ func (x *CreateModuleRequest) GetName() string { return "" } -func (x *CreateModuleRequest) GetPath() string { +func (x *CreateModuleRequest) GetDir() string { if x != nil { - return x.Path + return x.Dir } return "" } @@ -499,7 +511,7 @@ type ModuleConfigDefaultsRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Dir string `protobuf:"bytes,1,opt,name=dir,proto3" json:"dir,omitempty"` } func (x *ModuleConfigDefaultsRequest) Reset() { @@ -532,9 +544,9 @@ func (*ModuleConfigDefaultsRequest) Descriptor() ([]byte, []int) { return file_xyz_block_ftl_v1_language_language_proto_rawDescGZIP(), []int{6} } -func (x *ModuleConfigDefaultsRequest) GetPath() string { +func (x *ModuleConfigDefaultsRequest) GetDir() string { if x != nil { - return x.Path + return x.Dir } return "" } @@ -552,12 +564,12 @@ type ModuleConfigDefaultsResponse struct { unknownFields protoimpl.UnknownFields // Default relative path to the directory containing all build artifacts for deployments - DeployDir string `protobuf:"bytes,1,opt,name=deployDir,proto3" json:"deployDir,omitempty"` + DeployDir string `protobuf:"bytes,1,opt,name=deploy_dir,json=deployDir,proto3" json:"deploy_dir,omitempty"` // Default build command Build *string `protobuf:"bytes,2,opt,name=build,proto3,oneof" json:"build,omitempty"` // Default relative path to the directory containing generated schema files GeneratedSchemaDir *string `protobuf:"bytes,3,opt,name=generated_schema_dir,json=generatedSchemaDir,proto3,oneof" json:"generated_schema_dir,omitempty"` - // Default patterns to watch for file changes + // Default patterns to watch for file changes, relative to the module directory Watch []string `protobuf:"bytes,4,rep,name=watch,proto3" json:"watch,omitempty"` // Default language specific configuration. // These defaults are filled in by looking at each root key only. If the key is not present, the default is used. @@ -1060,10 +1072,12 @@ type BuildRequest struct { unknownFields protoimpl.UnknownFields // The root path for the FTL project - ProjectPath string `protobuf:"bytes,1,opt,name=project_path,json=projectPath,proto3" json:"project_path,omitempty"` + ProjectRoot string `protobuf:"bytes,1,opt,name=project_root,json=projectRoot,proto3" json:"project_root,omitempty"` + // The path to the directory containing all module stubs. Each module stub is in a subdirectory. + StubsRoot string `protobuf:"bytes,2,opt,name=stubs_root,json=stubsRoot,proto3" json:"stubs_root,omitempty"` // Indicates whether to watch for file changes and automatically rebuild - RebuildAutomatically bool `protobuf:"varint,2,opt,name=rebuild_automatically,json=rebuildAutomatically,proto3" json:"rebuild_automatically,omitempty"` - BuildContext *BuildContext `protobuf:"bytes,3,opt,name=build_context,json=buildContext,proto3" json:"build_context,omitempty"` + RebuildAutomatically bool `protobuf:"varint,3,opt,name=rebuild_automatically,json=rebuildAutomatically,proto3" json:"rebuild_automatically,omitempty"` + BuildContext *BuildContext `protobuf:"bytes,4,opt,name=build_context,json=buildContext,proto3" json:"build_context,omitempty"` } func (x *BuildRequest) Reset() { @@ -1096,9 +1110,16 @@ func (*BuildRequest) Descriptor() ([]byte, []int) { return file_xyz_block_ftl_v1_language_language_proto_rawDescGZIP(), []int{16} } -func (x *BuildRequest) GetProjectPath() string { +func (x *BuildRequest) GetProjectRoot() string { if x != nil { - return x.ProjectPath + return x.ProjectRoot + } + return "" +} + +func (x *BuildRequest) GetStubsRoot() string { + if x != nil { + return x.StubsRoot } return "" } @@ -1504,6 +1525,216 @@ func (*BuildEvent_BuildFailure) isBuildEvent_Event() {} func (*BuildEvent_LogMessage) isBuildEvent_Event() {} +type GenerateStubsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The directory path to generate stubs into + Dir string `protobuf:"bytes,1,opt,name=dir,proto3" json:"dir,omitempty"` + // The schema of the module to generate stubs for + Module *schema.Module `protobuf:"bytes,2,opt,name=module,proto3" json:"module,omitempty"` + // The module's configuration to generate stubs for + ModuleConfig *ModuleConfig `protobuf:"bytes,3,opt,name=module_config,json=moduleConfig,proto3" json:"module_config,omitempty"` + // Native module configuration is the configuration for a module that uses the plugin's language, if + // the main moduleConfig provided is of a different language. It is provided as a mechanism to derive + // language specific information. For example, the language version. + NativeModuleConfig *ModuleConfig `protobuf:"bytes,4,opt,name=native_module_config,json=nativeModuleConfig,proto3,oneof" json:"native_module_config,omitempty"` +} + +func (x *GenerateStubsRequest) Reset() { + *x = GenerateStubsRequest{} + mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GenerateStubsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateStubsRequest) ProtoMessage() {} + +func (x *GenerateStubsRequest) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateStubsRequest.ProtoReflect.Descriptor instead. +func (*GenerateStubsRequest) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_v1_language_language_proto_rawDescGZIP(), []int{22} +} + +func (x *GenerateStubsRequest) GetDir() string { + if x != nil { + return x.Dir + } + return "" +} + +func (x *GenerateStubsRequest) GetModule() *schema.Module { + if x != nil { + return x.Module + } + return nil +} + +func (x *GenerateStubsRequest) GetModuleConfig() *ModuleConfig { + if x != nil { + return x.ModuleConfig + } + return nil +} + +func (x *GenerateStubsRequest) GetNativeModuleConfig() *ModuleConfig { + if x != nil { + return x.NativeModuleConfig + } + return nil +} + +type GenerateStubsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GenerateStubsResponse) Reset() { + *x = GenerateStubsResponse{} + mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GenerateStubsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateStubsResponse) ProtoMessage() {} + +func (x *GenerateStubsResponse) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateStubsResponse.ProtoReflect.Descriptor instead. +func (*GenerateStubsResponse) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_v1_language_language_proto_rawDescGZIP(), []int{23} +} + +type SyncStubReferencesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ModuleConfig *ModuleConfig `protobuf:"bytes,1,opt,name=module_config,json=moduleConfig,proto3" json:"module_config,omitempty"` + // The path of the directory containing all module stubs. Each module is in a subdirectory + StubsRoot string `protobuf:"bytes,2,opt,name=stubs_root,json=stubsRoot,proto3" json:"stubs_root,omitempty"` + // The names of all modules that have had stubs generated + Modules []string `protobuf:"bytes,3,rep,name=modules,proto3" json:"modules,omitempty"` +} + +func (x *SyncStubReferencesRequest) Reset() { + *x = SyncStubReferencesRequest{} + mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStubReferencesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStubReferencesRequest) ProtoMessage() {} + +func (x *SyncStubReferencesRequest) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[24] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStubReferencesRequest.ProtoReflect.Descriptor instead. +func (*SyncStubReferencesRequest) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_v1_language_language_proto_rawDescGZIP(), []int{24} +} + +func (x *SyncStubReferencesRequest) GetModuleConfig() *ModuleConfig { + if x != nil { + return x.ModuleConfig + } + return nil +} + +func (x *SyncStubReferencesRequest) GetStubsRoot() string { + if x != nil { + return x.StubsRoot + } + return "" +} + +func (x *SyncStubReferencesRequest) GetModules() []string { + if x != nil { + return x.Modules + } + return nil +} + +type SyncStubReferencesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SyncStubReferencesResponse) Reset() { + *x = SyncStubReferencesResponse{} + mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStubReferencesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStubReferencesResponse) ProtoMessage() {} + +func (x *SyncStubReferencesResponse) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[25] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStubReferencesResponse.ProtoReflect.Descriptor instead. +func (*SyncStubReferencesResponse) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_v1_language_language_proto_rawDescGZIP(), []int{25} +} + type GetCreateModuleFlagsResponse_Flag struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1520,7 +1751,7 @@ type GetCreateModuleFlagsResponse_Flag struct { func (x *GetCreateModuleFlagsResponse_Flag) Reset() { *x = GetCreateModuleFlagsResponse_Flag{} - mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[22] + mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1532,7 +1763,7 @@ func (x *GetCreateModuleFlagsResponse_Flag) String() string { func (*GetCreateModuleFlagsResponse_Flag) ProtoMessage() {} func (x *GetCreateModuleFlagsResponse_Flag) ProtoReflect() protoreflect.Message { - mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[22] + mi := &file_xyz_block_ftl_v1_language_language_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1603,281 +1834,332 @@ var file_xyz_block_ftl_v1_language_language_proto_rawDesc = []byte{ 0x74, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x74, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x24, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa1, 0x02, 0x0a, 0x0c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbc, 0x02, 0x0a, 0x0c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1c, - 0x0a, 0x09, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x69, 0x72, 0x12, 0x19, 0x0a, 0x05, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x64, 0x69, 0x72, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x12, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x65, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x44, 0x69, 0x72, 0x88, 0x01, 0x01, 0x12, 0x14, - 0x0a, 0x05, 0x77, 0x61, 0x74, 0x63, 0x68, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x77, - 0x61, 0x74, 0x63, 0x68, 0x12, 0x40, 0x0a, 0x0f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x64, 0x69, 0x72, 0x22, 0x66, 0x0a, 0x0d, 0x50, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6e, 0x6f, 0x5f, 0x67, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x05, 0x6e, 0x6f, 0x47, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x72, - 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x65, 0x72, 0x6d, 0x69, - 0x74, 0x22, 0x39, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x22, 0xcf, 0x02, 0x0a, - 0x1c, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, - 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x78, - 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x6c, 0x61, 0x67, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, - 0x73, 0x1a, 0xda, 0x01, 0x0a, 0x04, 0x46, 0x6c, 0x61, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x65, - 0x6c, 0x70, 0x12, 0x19, 0x0a, 0x05, 0x65, 0x6e, 0x76, 0x61, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x61, 0x72, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, - 0x05, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05, - 0x73, 0x68, 0x6f, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x63, - 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, - 0x0b, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, - 0x1d, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x03, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x88, 0x01, 0x01, 0x42, 0x08, - 0x0a, 0x06, 0x5f, 0x65, 0x6e, 0x76, 0x61, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x68, 0x6f, - 0x72, 0x74, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, - 0x65, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0xbd, - 0x01, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x4f, - 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, - 0x67, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x2d, 0x0a, 0x05, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x05, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x16, - 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x0a, 0x1b, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x89, 0x02, 0x0a, 0x1c, 0x4d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, - 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x69, 0x72, 0x12, 0x19, 0x0a, 0x05, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x01, 0x52, 0x12, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x44, 0x69, 0x72, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x61, - 0x74, 0x63, 0x68, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x77, 0x61, 0x74, 0x63, 0x68, - 0x12, 0x40, 0x0a, 0x0f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x52, 0x0e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x17, 0x0a, 0x15, - 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x5f, 0x64, 0x69, 0x72, 0x22, 0x63, 0x0a, 0x13, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, - 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, - 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x1a, 0x0a, 0x08, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x6c, + 0x6f, 0x79, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, + 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x69, 0x72, 0x12, 0x19, 0x0a, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x88, + 0x01, 0x01, 0x12, 0x35, 0x0a, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, + 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x01, 0x52, 0x12, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x53, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x44, 0x69, 0x72, 0x88, 0x01, 0x01, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x61, 0x74, + 0x63, 0x68, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x77, 0x61, 0x74, 0x63, 0x68, 0x12, + 0x40, 0x0a, 0x0f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x52, 0x0e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x5f, 0x64, 0x69, 0x72, 0x22, 0x64, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x6e, + 0x6f, 0x5f, 0x67, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x6e, 0x6f, 0x47, + 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x68, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x22, 0x39, 0x0a, 0x1b, 0x47, 0x65, + 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, + 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x22, 0xcf, 0x02, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, + 0x65, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, + 0x6c, 0x61, 0x67, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x1a, 0xda, 0x01, 0x0a, 0x04, 0x46, + 0x6c, 0x61, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x12, 0x19, 0x0a, 0x05, 0x65, + 0x6e, 0x76, 0x61, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6e, + 0x76, 0x61, 0x72, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x88, 0x01, + 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x0b, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, + 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x07, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x65, 0x6e, 0x76, 0x61, + 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, + 0x70, 0x6c, 0x61, 0x63, 0x65, 0x68, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0xbb, 0x01, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x4f, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, + 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x05, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x05, + 0x46, 0x6c, 0x61, 0x67, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x0a, + 0x1b, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x69, 0x72, 0x22, 0x8a, + 0x02, 0x0a, 0x1c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x44, 0x69, 0x72, 0x12, 0x19, + 0x0a, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x05, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x14, 0x67, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x64, 0x69, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x12, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x44, 0x69, 0x72, 0x88, 0x01, 0x01, + 0x12, 0x14, 0x0a, 0x05, 0x77, 0x61, 0x74, 0x63, 0x68, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x05, 0x77, 0x61, 0x74, 0x63, 0x68, 0x12, 0x40, 0x0a, 0x0f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, + 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, + 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x64, 0x69, 0x72, 0x22, 0x63, 0x0a, 0x13, 0x44, + 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x22, 0x30, 0x0a, 0x14, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x65, + 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x22, 0x69, + 0x0a, 0x1a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x0c, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x1d, 0x0a, 0x1b, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6d, 0x73, 0x67, 0x12, 0x41, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, - 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x30, 0x0a, 0x14, 0x44, 0x65, - 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, - 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x4c, 0x0a, - 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, + 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x35, 0x0a, 0x03, 0x70, 0x6f, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, - 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x37, 0x0a, 0x06, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, - 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, - 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x22, 0x69, 0x0a, 0x1a, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, - 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x78, 0x74, 0x22, 0x1d, 0x0a, 0x1b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0xc0, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, - 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x41, - 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, + 0x2e, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x70, 0x6f, 0x73, 0x22, 0x2b, + 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x08, 0x0a, 0x04, + 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x01, + 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x22, 0x7a, 0x0a, 0x08, 0x50, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x64, + 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x65, 0x6e, + 0x64, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x22, 0x45, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0xd3, + 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x6f, + 0x6f, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x75, 0x62, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x75, 0x62, 0x73, 0x52, 0x6f, 0x6f, + 0x74, 0x12, 0x33, 0x0a, 0x15, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x61, 0x75, 0x74, + 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x14, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, + 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x12, 0x4c, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, - 0x6c, 0x12, 0x35, 0x0a, 0x03, 0x70, 0x6f, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, - 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x50, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x70, 0x6f, 0x73, 0x22, 0x2b, 0x0a, 0x0a, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, - 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, - 0x52, 0x4f, 0x52, 0x10, 0x02, 0x22, 0x7a, 0x0a, 0x08, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x6c, 0x69, 0x6e, - 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6c, - 0x75, 0x6d, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6c, 0x75, 0x6d, - 0x6e, 0x22, 0x45, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x38, - 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x22, 0x33, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x22, 0x91, 0x02, 0x0a, 0x0c, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, + 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x37, 0x0a, 0x06, 0x6d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x12, 0x21, 0x0a, 0x0c, + 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, + 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0xd6, 0x01, + 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, + 0x14, 0x69, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x65, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x73, 0x41, + 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, + 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x37, 0x0a, + 0x17, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, + 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x22, 0xa2, 0x01, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x44, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0xb4, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x33, 0x0a, 0x15, - 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, - 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x72, 0x65, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, - 0x79, 0x12, 0x4c, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, + 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x34, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, + 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, + 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x02, + 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x22, 0xe2, 0x02, 0x0a, 0x0a, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x61, 0x0a, 0x14, 0x61, 0x75, + 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, - 0x33, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x49, 0x64, 0x22, 0x91, 0x02, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x6d, - 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x12, 0x69, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x52, - 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x06, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x63, 0x6b, 0x65, - 0x72, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x6f, 0x63, 0x6b, 0x65, 0x72, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, - 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, - 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0xd6, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x61, - 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, - 0x74, 0x69, 0x63, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, - 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, - 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x37, 0x0a, 0x17, 0x69, 0x6e, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, - 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, - 0x73, 0x22, 0xa2, 0x01, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x44, 0x0a, 0x05, 0x6c, 0x65, - 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x48, 0x00, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x52, + 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4e, 0x0a, + 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, + 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4e, 0x0a, + 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x48, 0x00, 0x52, + 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x48, 0x0a, + 0x0b, 0x6c, 0x6f, 0x67, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4c, + 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x6c, 0x6f, 0x67, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x22, 0xa8, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, + 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x37, 0x0a, 0x06, 0x6d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x5e, 0x0a, 0x14, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x12, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, + 0x01, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa2, 0x01, 0x0a, 0x19, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, + 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, - 0x22, 0x34, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, - 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, - 0x01, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, - 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x22, 0xe2, 0x02, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x61, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x72, 0x65, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x75, 0x62, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x75, 0x62, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x79, 0x6e, + 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb0, 0x08, 0x0a, 0x0f, 0x4c, 0x61, 0x6e, 0x67, + 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, + 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, + 0x12, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x6f, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x12, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, - 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, - 0x65, 0x64, 0x48, 0x00, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4e, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x48, 0x0a, 0x0b, 0x6c, 0x6f, 0x67, 0x5f, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, - 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x6c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x32, 0xb8, 0x06, 0x0a, 0x0f, - 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x87, 0x01, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, - 0x6c, 0x61, 0x67, 0x73, 0x12, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, - 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x4d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, - 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, - 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x72, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, - 0x69, 0x65, 0x73, 0x12, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x0f, + 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, + 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x44, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x44, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x59, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x84, 0x01, 0x0a, 0x13, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x12, 0x35, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, - 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x72, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, + 0x75, 0x62, 0x73, 0x12, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, - 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x27, 0x2e, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x53, + 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x34, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, - 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, - 0x84, 0x01, 0x0a, 0x13, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x35, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, - 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, - 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x52, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x42, 0x44, 0x35, 0x34, 0x35, 0x36, 0x36, 0x39, - 0x37, 0x35, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, - 0x66, 0x74, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3b, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, + 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, + 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x52, 0x50, 0x01, 0x5a, 0x4e, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x42, 0x44, 0x35, 0x34, + 0x35, 0x36, 0x36, 0x39, 0x37, 0x35, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, + 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x3b, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1893,7 +2175,7 @@ func file_xyz_block_ftl_v1_language_language_proto_rawDescGZIP() []byte { } var file_xyz_block_ftl_v1_language_language_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_xyz_block_ftl_v1_language_language_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_xyz_block_ftl_v1_language_language_proto_msgTypes = make([]protoimpl.MessageInfo, 27) var file_xyz_block_ftl_v1_language_language_proto_goTypes = []any{ (Error_ErrorLevel)(0), // 0: xyz.block.ftl.v1.language.Error.ErrorLevel (LogMessage_LogLevel)(0), // 1: xyz.block.ftl.v1.language.LogMessage.LogLevel @@ -1919,28 +2201,32 @@ var file_xyz_block_ftl_v1_language_language_proto_goTypes = []any{ (*BuildFailure)(nil), // 21: xyz.block.ftl.v1.language.BuildFailure (*LogMessage)(nil), // 22: xyz.block.ftl.v1.language.LogMessage (*BuildEvent)(nil), // 23: xyz.block.ftl.v1.language.BuildEvent - (*GetCreateModuleFlagsResponse_Flag)(nil), // 24: xyz.block.ftl.v1.language.GetCreateModuleFlagsResponse.Flag - (*structpb.Struct)(nil), // 25: google.protobuf.Struct - (*schema.Schema)(nil), // 26: xyz.block.ftl.v1.schema.Schema - (*schema.Module)(nil), // 27: xyz.block.ftl.v1.schema.Module - (*v1.PingRequest)(nil), // 28: xyz.block.ftl.v1.PingRequest - (*v1.PingResponse)(nil), // 29: xyz.block.ftl.v1.PingResponse + (*GenerateStubsRequest)(nil), // 24: xyz.block.ftl.v1.language.GenerateStubsRequest + (*GenerateStubsResponse)(nil), // 25: xyz.block.ftl.v1.language.GenerateStubsResponse + (*SyncStubReferencesRequest)(nil), // 26: xyz.block.ftl.v1.language.SyncStubReferencesRequest + (*SyncStubReferencesResponse)(nil), // 27: xyz.block.ftl.v1.language.SyncStubReferencesResponse + (*GetCreateModuleFlagsResponse_Flag)(nil), // 28: xyz.block.ftl.v1.language.GetCreateModuleFlagsResponse.Flag + (*structpb.Struct)(nil), // 29: google.protobuf.Struct + (*schema.Schema)(nil), // 30: xyz.block.ftl.v1.schema.Schema + (*schema.Module)(nil), // 31: xyz.block.ftl.v1.schema.Module + (*v1.PingRequest)(nil), // 32: xyz.block.ftl.v1.PingRequest + (*v1.PingResponse)(nil), // 33: xyz.block.ftl.v1.PingResponse } var file_xyz_block_ftl_v1_language_language_proto_depIdxs = []int32{ - 25, // 0: xyz.block.ftl.v1.language.ModuleConfig.language_config:type_name -> google.protobuf.Struct - 24, // 1: xyz.block.ftl.v1.language.GetCreateModuleFlagsResponse.flags:type_name -> xyz.block.ftl.v1.language.GetCreateModuleFlagsResponse.Flag + 29, // 0: xyz.block.ftl.v1.language.ModuleConfig.language_config:type_name -> google.protobuf.Struct + 28, // 1: xyz.block.ftl.v1.language.GetCreateModuleFlagsResponse.flags:type_name -> xyz.block.ftl.v1.language.GetCreateModuleFlagsResponse.Flag 3, // 2: xyz.block.ftl.v1.language.CreateModuleRequest.project_config:type_name -> xyz.block.ftl.v1.language.ProjectConfig - 25, // 3: xyz.block.ftl.v1.language.CreateModuleRequest.Flags:type_name -> google.protobuf.Struct - 25, // 4: xyz.block.ftl.v1.language.ModuleConfigDefaultsResponse.language_config:type_name -> google.protobuf.Struct + 29, // 3: xyz.block.ftl.v1.language.CreateModuleRequest.Flags:type_name -> google.protobuf.Struct + 29, // 4: xyz.block.ftl.v1.language.ModuleConfigDefaultsResponse.language_config:type_name -> google.protobuf.Struct 2, // 5: xyz.block.ftl.v1.language.DependenciesRequest.module_config:type_name -> xyz.block.ftl.v1.language.ModuleConfig 2, // 6: xyz.block.ftl.v1.language.BuildContext.module_config:type_name -> xyz.block.ftl.v1.language.ModuleConfig - 26, // 7: xyz.block.ftl.v1.language.BuildContext.schema:type_name -> xyz.block.ftl.v1.schema.Schema + 30, // 7: xyz.block.ftl.v1.language.BuildContext.schema:type_name -> xyz.block.ftl.v1.schema.Schema 12, // 8: xyz.block.ftl.v1.language.BuildContextUpdatedRequest.buildContext:type_name -> xyz.block.ftl.v1.language.BuildContext 0, // 9: xyz.block.ftl.v1.language.Error.level:type_name -> xyz.block.ftl.v1.language.Error.ErrorLevel 16, // 10: xyz.block.ftl.v1.language.Error.pos:type_name -> xyz.block.ftl.v1.language.Position 15, // 11: xyz.block.ftl.v1.language.ErrorList.errors:type_name -> xyz.block.ftl.v1.language.Error 12, // 12: xyz.block.ftl.v1.language.BuildRequest.build_context:type_name -> xyz.block.ftl.v1.language.BuildContext - 27, // 13: xyz.block.ftl.v1.language.BuildSuccess.module:type_name -> xyz.block.ftl.v1.schema.Module + 31, // 13: xyz.block.ftl.v1.language.BuildSuccess.module:type_name -> xyz.block.ftl.v1.schema.Module 17, // 14: xyz.block.ftl.v1.language.BuildSuccess.errors:type_name -> xyz.block.ftl.v1.language.ErrorList 17, // 15: xyz.block.ftl.v1.language.BuildFailure.errors:type_name -> xyz.block.ftl.v1.language.ErrorList 1, // 16: xyz.block.ftl.v1.language.LogMessage.level:type_name -> xyz.block.ftl.v1.language.LogMessage.LogLevel @@ -1948,25 +2234,33 @@ var file_xyz_block_ftl_v1_language_language_proto_depIdxs = []int32{ 20, // 18: xyz.block.ftl.v1.language.BuildEvent.build_success:type_name -> xyz.block.ftl.v1.language.BuildSuccess 21, // 19: xyz.block.ftl.v1.language.BuildEvent.build_failure:type_name -> xyz.block.ftl.v1.language.BuildFailure 22, // 20: xyz.block.ftl.v1.language.BuildEvent.log_message:type_name -> xyz.block.ftl.v1.language.LogMessage - 28, // 21: xyz.block.ftl.v1.language.LanguageService.Ping:input_type -> xyz.block.ftl.v1.PingRequest - 4, // 22: xyz.block.ftl.v1.language.LanguageService.GetCreateModuleFlags:input_type -> xyz.block.ftl.v1.language.GetCreateModuleFlagsRequest - 6, // 23: xyz.block.ftl.v1.language.LanguageService.CreateModule:input_type -> xyz.block.ftl.v1.language.CreateModuleRequest - 8, // 24: xyz.block.ftl.v1.language.LanguageService.ModuleConfigDefaults:input_type -> xyz.block.ftl.v1.language.ModuleConfigDefaultsRequest - 10, // 25: xyz.block.ftl.v1.language.LanguageService.GetDependencies:input_type -> xyz.block.ftl.v1.language.DependenciesRequest - 18, // 26: xyz.block.ftl.v1.language.LanguageService.Build:input_type -> xyz.block.ftl.v1.language.BuildRequest - 13, // 27: xyz.block.ftl.v1.language.LanguageService.BuildContextUpdated:input_type -> xyz.block.ftl.v1.language.BuildContextUpdatedRequest - 29, // 28: xyz.block.ftl.v1.language.LanguageService.Ping:output_type -> xyz.block.ftl.v1.PingResponse - 5, // 29: xyz.block.ftl.v1.language.LanguageService.GetCreateModuleFlags:output_type -> xyz.block.ftl.v1.language.GetCreateModuleFlagsResponse - 7, // 30: xyz.block.ftl.v1.language.LanguageService.CreateModule:output_type -> xyz.block.ftl.v1.language.CreateModuleResponse - 9, // 31: xyz.block.ftl.v1.language.LanguageService.ModuleConfigDefaults:output_type -> xyz.block.ftl.v1.language.ModuleConfigDefaultsResponse - 11, // 32: xyz.block.ftl.v1.language.LanguageService.GetDependencies:output_type -> xyz.block.ftl.v1.language.DependenciesResponse - 23, // 33: xyz.block.ftl.v1.language.LanguageService.Build:output_type -> xyz.block.ftl.v1.language.BuildEvent - 14, // 34: xyz.block.ftl.v1.language.LanguageService.BuildContextUpdated:output_type -> xyz.block.ftl.v1.language.BuildContextUpdatedResponse - 28, // [28:35] is the sub-list for method output_type - 21, // [21:28] is the sub-list for method input_type - 21, // [21:21] is the sub-list for extension type_name - 21, // [21:21] is the sub-list for extension extendee - 0, // [0:21] is the sub-list for field type_name + 31, // 21: xyz.block.ftl.v1.language.GenerateStubsRequest.module:type_name -> xyz.block.ftl.v1.schema.Module + 2, // 22: xyz.block.ftl.v1.language.GenerateStubsRequest.module_config:type_name -> xyz.block.ftl.v1.language.ModuleConfig + 2, // 23: xyz.block.ftl.v1.language.GenerateStubsRequest.native_module_config:type_name -> xyz.block.ftl.v1.language.ModuleConfig + 2, // 24: xyz.block.ftl.v1.language.SyncStubReferencesRequest.module_config:type_name -> xyz.block.ftl.v1.language.ModuleConfig + 32, // 25: xyz.block.ftl.v1.language.LanguageService.Ping:input_type -> xyz.block.ftl.v1.PingRequest + 4, // 26: xyz.block.ftl.v1.language.LanguageService.GetCreateModuleFlags:input_type -> xyz.block.ftl.v1.language.GetCreateModuleFlagsRequest + 6, // 27: xyz.block.ftl.v1.language.LanguageService.CreateModule:input_type -> xyz.block.ftl.v1.language.CreateModuleRequest + 8, // 28: xyz.block.ftl.v1.language.LanguageService.ModuleConfigDefaults:input_type -> xyz.block.ftl.v1.language.ModuleConfigDefaultsRequest + 10, // 29: xyz.block.ftl.v1.language.LanguageService.GetDependencies:input_type -> xyz.block.ftl.v1.language.DependenciesRequest + 18, // 30: xyz.block.ftl.v1.language.LanguageService.Build:input_type -> xyz.block.ftl.v1.language.BuildRequest + 13, // 31: xyz.block.ftl.v1.language.LanguageService.BuildContextUpdated:input_type -> xyz.block.ftl.v1.language.BuildContextUpdatedRequest + 24, // 32: xyz.block.ftl.v1.language.LanguageService.GenerateStubs:input_type -> xyz.block.ftl.v1.language.GenerateStubsRequest + 26, // 33: xyz.block.ftl.v1.language.LanguageService.SyncStubReferences:input_type -> xyz.block.ftl.v1.language.SyncStubReferencesRequest + 33, // 34: xyz.block.ftl.v1.language.LanguageService.Ping:output_type -> xyz.block.ftl.v1.PingResponse + 5, // 35: xyz.block.ftl.v1.language.LanguageService.GetCreateModuleFlags:output_type -> xyz.block.ftl.v1.language.GetCreateModuleFlagsResponse + 7, // 36: xyz.block.ftl.v1.language.LanguageService.CreateModule:output_type -> xyz.block.ftl.v1.language.CreateModuleResponse + 9, // 37: xyz.block.ftl.v1.language.LanguageService.ModuleConfigDefaults:output_type -> xyz.block.ftl.v1.language.ModuleConfigDefaultsResponse + 11, // 38: xyz.block.ftl.v1.language.LanguageService.GetDependencies:output_type -> xyz.block.ftl.v1.language.DependenciesResponse + 23, // 39: xyz.block.ftl.v1.language.LanguageService.Build:output_type -> xyz.block.ftl.v1.language.BuildEvent + 14, // 40: xyz.block.ftl.v1.language.LanguageService.BuildContextUpdated:output_type -> xyz.block.ftl.v1.language.BuildContextUpdatedResponse + 25, // 41: xyz.block.ftl.v1.language.LanguageService.GenerateStubs:output_type -> xyz.block.ftl.v1.language.GenerateStubsResponse + 27, // 42: xyz.block.ftl.v1.language.LanguageService.SyncStubReferences:output_type -> xyz.block.ftl.v1.language.SyncStubReferencesResponse + 34, // [34:43] is the sub-list for method output_type + 25, // [25:34] is the sub-list for method input_type + 25, // [25:25] is the sub-list for extension type_name + 25, // [25:25] is the sub-list for extension extendee + 0, // [0:25] is the sub-list for field type_name } func init() { file_xyz_block_ftl_v1_language_language_proto_init() } @@ -1983,13 +2277,14 @@ func file_xyz_block_ftl_v1_language_language_proto_init() { (*BuildEvent_LogMessage)(nil), } file_xyz_block_ftl_v1_language_language_proto_msgTypes[22].OneofWrappers = []any{} + file_xyz_block_ftl_v1_language_language_proto_msgTypes[26].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_xyz_block_ftl_v1_language_language_proto_rawDesc, NumEnums: 2, - NumMessages: 23, + NumMessages: 27, NumExtensions: 0, NumServices: 1, }, diff --git a/backend/protos/xyz/block/ftl/v1/language/language.proto b/backend/protos/xyz/block/ftl/v1/language/language.proto index 2457e2e406..16af872ca5 100644 --- a/backend/protos/xyz/block/ftl/v1/language/language.proto +++ b/backend/protos/xyz/block/ftl/v1/language/language.proto @@ -11,25 +11,30 @@ option java_multiple_files = true; // ModuleConfig contains the configuration for a module, found in the module's ftl.toml file. message ModuleConfig { - // name of the module + // Name of the module string name = 1; - // absolute path to the module's directory - string path = 2; - - // absolute path - string deployDir = 3; - optional string build = 4; - optional string generated_schema_dir = 5; - repeated string watch = 6; + // Absolute path to the module's directory + string dir = 2; + // The language of the module + string language = 3; + + // Absolute path to the directory containing all of this module's build artifacts for deployments + string deploy_dir = 4; + // Build is the command to build the module. + optional string build = 5; + // The directory to generate protobuf schema files into. These can be picked up by language specific build tools + optional string generated_schema_dir = 6; + // Patterns to watch for file changes + repeated string watch = 7; // LanguageConfig contains any metadata specific to a specific language. // These are stored in the ftl.toml file under the same key as the language (eg: "go", "java") - google.protobuf.Struct language_config = 7; + google.protobuf.Struct language_config = 8; } // ProjectConfig contains the configuration for a project, found in the ftl-project.toml file. message ProjectConfig { - string path = 1; + string dir = 1; string name = 2; bool no_git = 3; bool hermit = 4; @@ -55,9 +60,9 @@ message GetCreateModuleFlagsResponse { // Request to create a new module. message CreateModuleRequest { string name = 1; - // The root path for the module, which does not yet exist. + // The root directory for the module, which does not yet exist. // The plugin should create the directory. - string path = 2; + string dir = 2; // The project configuration ProjectConfig project_config = 3; @@ -70,7 +75,7 @@ message CreateModuleRequest { message CreateModuleResponse {} message ModuleConfigDefaultsRequest { - string path = 1; + string dir = 1; } // ModuleConfigDefaultsResponse provides defaults for ModuleConfig. @@ -82,7 +87,7 @@ message ModuleConfigDefaultsRequest { // the module defaults will not be recalculated. message ModuleConfigDefaultsResponse { // Default relative path to the directory containing all build artifacts for deployments - string deployDir = 1; + string deploy_dir = 1; // Default build command optional string build = 2; @@ -90,7 +95,7 @@ message ModuleConfigDefaultsResponse { // Default relative path to the directory containing generated schema files optional string generated_schema_dir = 3; - // Default patterns to watch for file changes + // Default patterns to watch for file changes, relative to the module directory repeated string watch = 4; // Default language specific configuration. @@ -154,11 +159,15 @@ message ErrorList { // Request to build a module. message BuildRequest { // The root path for the FTL project - string project_path = 1; + string project_root = 1; + + // The path to the directory containing all module stubs. Each module stub is in a subdirectory. + string stubs_root = 2; + // Indicates whether to watch for file changes and automatically rebuild - bool rebuild_automatically = 2; + bool rebuild_automatically = 3; - BuildContext build_context = 3; + BuildContext build_context = 4; } // AutoRebuildStarted should be sent when the plugin decides to start rebuilding automatically. @@ -233,6 +242,34 @@ message BuildEvent { } } +message GenerateStubsRequest { + // The directory path to generate stubs into + string dir = 1; + // The schema of the module to generate stubs for + schema.Module module = 2; + // The module's configuration to generate stubs for + ModuleConfig module_config = 3; + + // Native module configuration is the configuration for a module that uses the plugin's language, if + // the main moduleConfig provided is of a different language. It is provided as a mechanism to derive + // language specific information. For example, the language version. + optional ModuleConfig native_module_config = 4; +} + +message GenerateStubsResponse {} + +message SyncStubReferencesRequest { + ModuleConfig module_config = 1; + + // The path of the directory containing all module stubs. Each module is in a subdirectory + string stubs_root = 2; + + // The names of all modules that have had stubs generated + repeated string modules = 3; +} + +message SyncStubReferencesResponse {} + // LanguageService allows a plugin to add support for a programming language. service LanguageService { // Ping service for readiness. @@ -270,4 +307,22 @@ service LanguageService { // Each time this call is made, the Build call must send back a corresponding BuildSuccess or BuildFailure // event with the updated build context id with "is_automatic_rebuild" as false. rpc BuildContextUpdated(BuildContextUpdatedRequest) returns (BuildContextUpdatedResponse); + + // Generate stubs for a module. + // + // Stubs allow modules to import other module's exported interface. If a language does not need this step, + // then it is not required to do anything in this call. + // + // This call is not tied to the module that this plugin is responsible for. A plugin of each language will + // be chosen to generate stubs for each module. + rpc GenerateStubs(GenerateStubsRequest) returns (GenerateStubsResponse); + + // SyncStubReferences is called when module stubs have been updated. This allows the plugin to update + // references to external modules, regardless of whether they are dependencies. + // + // For example, go plugin adds references to all modules into the go.work file so that tools can automatically + // import the modules when users start reference them. + // + // It is optional to do anything with this call. + rpc SyncStubReferences(SyncStubReferencesRequest) returns (SyncStubReferencesResponse); } diff --git a/backend/protos/xyz/block/ftl/v1/language/languagepbconnect/language.connect.go b/backend/protos/xyz/block/ftl/v1/language/languagepbconnect/language.connect.go index ac1af73ef8..eb1af1073d 100644 --- a/backend/protos/xyz/block/ftl/v1/language/languagepbconnect/language.connect.go +++ b/backend/protos/xyz/block/ftl/v1/language/languagepbconnect/language.connect.go @@ -53,6 +53,12 @@ const ( // LanguageServiceBuildContextUpdatedProcedure is the fully-qualified name of the LanguageService's // BuildContextUpdated RPC. LanguageServiceBuildContextUpdatedProcedure = "/xyz.block.ftl.v1.language.LanguageService/BuildContextUpdated" + // LanguageServiceGenerateStubsProcedure is the fully-qualified name of the LanguageService's + // GenerateStubs RPC. + LanguageServiceGenerateStubsProcedure = "/xyz.block.ftl.v1.language.LanguageService/GenerateStubs" + // LanguageServiceSyncStubReferencesProcedure is the fully-qualified name of the LanguageService's + // SyncStubReferences RPC. + LanguageServiceSyncStubReferencesProcedure = "/xyz.block.ftl.v1.language.LanguageService/SyncStubReferences" ) // LanguageServiceClient is a client for the xyz.block.ftl.v1.language.LanguageService service. @@ -84,6 +90,22 @@ type LanguageServiceClient interface { // Each time this call is made, the Build call must send back a corresponding BuildSuccess or BuildFailure // event with the updated build context id with "is_automatic_rebuild" as false. BuildContextUpdated(context.Context, *connect.Request[language.BuildContextUpdatedRequest]) (*connect.Response[language.BuildContextUpdatedResponse], error) + // Generate stubs for a module. + // + // Stubs allow modules to import other module's exported interface. If a language does not need this step, + // then it is not required to do anything in this call. + // + // This call is not tied to the module that this plugin is responsible for. A plugin of each language will + // be chosen to generate stubs for each module. + GenerateStubs(context.Context, *connect.Request[language.GenerateStubsRequest]) (*connect.Response[language.GenerateStubsResponse], error) + // SyncStubReferences is called when module stubs have been updated. This allows the plugin to update + // references to external modules, regardless of whether they are dependencies. + // + // For example, go plugin adds references to all modules into the go.work file so that tools can automatically + // import the modules when users start reference them. + // + // It is optional to do anything with this call. + SyncStubReferences(context.Context, *connect.Request[language.SyncStubReferencesRequest]) (*connect.Response[language.SyncStubReferencesResponse], error) } // NewLanguageServiceClient constructs a client for the xyz.block.ftl.v1.language.LanguageService @@ -132,6 +154,16 @@ func NewLanguageServiceClient(httpClient connect.HTTPClient, baseURL string, opt baseURL+LanguageServiceBuildContextUpdatedProcedure, opts..., ), + generateStubs: connect.NewClient[language.GenerateStubsRequest, language.GenerateStubsResponse]( + httpClient, + baseURL+LanguageServiceGenerateStubsProcedure, + opts..., + ), + syncStubReferences: connect.NewClient[language.SyncStubReferencesRequest, language.SyncStubReferencesResponse]( + httpClient, + baseURL+LanguageServiceSyncStubReferencesProcedure, + opts..., + ), } } @@ -144,6 +176,8 @@ type languageServiceClient struct { getDependencies *connect.Client[language.DependenciesRequest, language.DependenciesResponse] build *connect.Client[language.BuildRequest, language.BuildEvent] buildContextUpdated *connect.Client[language.BuildContextUpdatedRequest, language.BuildContextUpdatedResponse] + generateStubs *connect.Client[language.GenerateStubsRequest, language.GenerateStubsResponse] + syncStubReferences *connect.Client[language.SyncStubReferencesRequest, language.SyncStubReferencesResponse] } // Ping calls xyz.block.ftl.v1.language.LanguageService.Ping. @@ -181,6 +215,16 @@ func (c *languageServiceClient) BuildContextUpdated(ctx context.Context, req *co return c.buildContextUpdated.CallUnary(ctx, req) } +// GenerateStubs calls xyz.block.ftl.v1.language.LanguageService.GenerateStubs. +func (c *languageServiceClient) GenerateStubs(ctx context.Context, req *connect.Request[language.GenerateStubsRequest]) (*connect.Response[language.GenerateStubsResponse], error) { + return c.generateStubs.CallUnary(ctx, req) +} + +// SyncStubReferences calls xyz.block.ftl.v1.language.LanguageService.SyncStubReferences. +func (c *languageServiceClient) SyncStubReferences(ctx context.Context, req *connect.Request[language.SyncStubReferencesRequest]) (*connect.Response[language.SyncStubReferencesResponse], error) { + return c.syncStubReferences.CallUnary(ctx, req) +} + // LanguageServiceHandler is an implementation of the xyz.block.ftl.v1.language.LanguageService // service. type LanguageServiceHandler interface { @@ -211,6 +255,22 @@ type LanguageServiceHandler interface { // Each time this call is made, the Build call must send back a corresponding BuildSuccess or BuildFailure // event with the updated build context id with "is_automatic_rebuild" as false. BuildContextUpdated(context.Context, *connect.Request[language.BuildContextUpdatedRequest]) (*connect.Response[language.BuildContextUpdatedResponse], error) + // Generate stubs for a module. + // + // Stubs allow modules to import other module's exported interface. If a language does not need this step, + // then it is not required to do anything in this call. + // + // This call is not tied to the module that this plugin is responsible for. A plugin of each language will + // be chosen to generate stubs for each module. + GenerateStubs(context.Context, *connect.Request[language.GenerateStubsRequest]) (*connect.Response[language.GenerateStubsResponse], error) + // SyncStubReferences is called when module stubs have been updated. This allows the plugin to update + // references to external modules, regardless of whether they are dependencies. + // + // For example, go plugin adds references to all modules into the go.work file so that tools can automatically + // import the modules when users start reference them. + // + // It is optional to do anything with this call. + SyncStubReferences(context.Context, *connect.Request[language.SyncStubReferencesRequest]) (*connect.Response[language.SyncStubReferencesResponse], error) } // NewLanguageServiceHandler builds an HTTP handler from the service implementation. It returns the @@ -255,6 +315,16 @@ func NewLanguageServiceHandler(svc LanguageServiceHandler, opts ...connect.Handl svc.BuildContextUpdated, opts..., ) + languageServiceGenerateStubsHandler := connect.NewUnaryHandler( + LanguageServiceGenerateStubsProcedure, + svc.GenerateStubs, + opts..., + ) + languageServiceSyncStubReferencesHandler := connect.NewUnaryHandler( + LanguageServiceSyncStubReferencesProcedure, + svc.SyncStubReferences, + opts..., + ) return "/xyz.block.ftl.v1.language.LanguageService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case LanguageServicePingProcedure: @@ -271,6 +341,10 @@ func NewLanguageServiceHandler(svc LanguageServiceHandler, opts ...connect.Handl languageServiceBuildHandler.ServeHTTP(w, r) case LanguageServiceBuildContextUpdatedProcedure: languageServiceBuildContextUpdatedHandler.ServeHTTP(w, r) + case LanguageServiceGenerateStubsProcedure: + languageServiceGenerateStubsHandler.ServeHTTP(w, r) + case LanguageServiceSyncStubReferencesProcedure: + languageServiceSyncStubReferencesHandler.ServeHTTP(w, r) default: http.NotFound(w, r) } @@ -307,3 +381,11 @@ func (UnimplementedLanguageServiceHandler) Build(context.Context, *connect.Reque func (UnimplementedLanguageServiceHandler) BuildContextUpdated(context.Context, *connect.Request[language.BuildContextUpdatedRequest]) (*connect.Response[language.BuildContextUpdatedResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.v1.language.LanguageService.BuildContextUpdated is not implemented")) } + +func (UnimplementedLanguageServiceHandler) GenerateStubs(context.Context, *connect.Request[language.GenerateStubsRequest]) (*connect.Response[language.GenerateStubsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.v1.language.LanguageService.GenerateStubs is not implemented")) +} + +func (UnimplementedLanguageServiceHandler) SyncStubReferences(context.Context, *connect.Request[language.SyncStubReferencesRequest]) (*connect.Response[language.SyncStubReferencesResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.v1.language.LanguageService.SyncStubReferences is not implemented")) +} diff --git a/backend/protos/xyz/block/ftl/v1/language/mixins.go b/backend/protos/xyz/block/ftl/v1/language/mixins.go index faddd24261..c12ed9bb8b 100644 --- a/backend/protos/xyz/block/ftl/v1/language/mixins.go +++ b/backend/protos/xyz/block/ftl/v1/language/mixins.go @@ -3,13 +3,20 @@ package languagepb import ( "fmt" + structpb "google.golang.org/protobuf/types/known/structpb" + "github.com/TBD54566975/ftl/internal/builderrors" "github.com/TBD54566975/ftl/internal/log" + "github.com/TBD54566975/ftl/internal/moduleconfig" + "github.com/TBD54566975/ftl/internal/projectconfig" "github.com/TBD54566975/ftl/internal/slices" ) // ErrorsFromProto converts a protobuf ErrorList to a []builderrors.Error. func ErrorsFromProto(e *ErrorList) []builderrors.Error { + if e == nil { + return []builderrors.Error{} + } return slices.Map(e.Errors, errorFromProto) } @@ -78,11 +85,90 @@ func LogLevelFromProto(level LogMessage_LogLevel) log.Level { switch level { case LogMessage_INFO: return log.Info + case LogMessage_DEBUG: + return log.Debug case LogMessage_WARN: return log.Warn case LogMessage_ERROR: return log.Error default: - panic(fmt.Sprintf("unhandled Log_Level %v", level)) + panic(fmt.Sprintf("unhandled log level %v", level)) + } +} + +func LogLevelToProto(level log.Level) LogMessage_LogLevel { + switch level { + case log.Info: + return LogMessage_INFO + case log.Debug: + return LogMessage_DEBUG + case log.Warn: + return LogMessage_WARN + case log.Error: + return LogMessage_ERROR + default: + panic(fmt.Sprintf("unhandled log level %v", level)) + } +} + +// ModuleConfigToProto converts a moduleconfig.AbsModuleConfig to a protobuf ModuleConfig. +// +// Absolute configs are used because relative paths may change resolve differently between parties. +func ModuleConfigToProto(config moduleconfig.AbsModuleConfig) (*ModuleConfig, error) { + proto := &ModuleConfig{ + Name: config.Module, + Dir: config.Dir, + DeployDir: config.DeployDir, + Watch: config.Watch, + Language: config.Language, + } + if config.Build != "" { + proto.Build = &config.Build + } + if config.GeneratedSchemaDir != "" { + proto.GeneratedSchemaDir = &config.GeneratedSchemaDir + } + + langConfigProto, err := structpb.NewStruct(config.LanguageConfig) + if err != nil { + return nil, fmt.Errorf("failed to marshal language config: %w", err) + } + proto.LanguageConfig = langConfigProto + return proto, nil + +} + +// ModuleConfigFromProto converts a protobuf ModuleConfig to a moduleconfig.AbsModuleConfig. +func ModuleConfigFromProto(proto *ModuleConfig) moduleconfig.AbsModuleConfig { + config := moduleconfig.AbsModuleConfig{ + Module: proto.Name, + Dir: proto.Dir, + DeployDir: proto.DeployDir, + Watch: proto.Watch, + Language: proto.Language, + Build: proto.GetBuild(), + GeneratedSchemaDir: proto.GetGeneratedSchemaDir(), + } + if proto.LanguageConfig != nil { + config.LanguageConfig = proto.LanguageConfig.AsMap() + } + return config +} + +func ProjectConfigToProto(projConfig projectconfig.Config) *ProjectConfig { + return &ProjectConfig{ + Dir: projConfig.Path, + Name: projConfig.Name, + NoGit: projConfig.NoGit, + Hermit: projConfig.Hermit, + } +} + +func ProjectConfigFromProto(proto *ProjectConfig) projectconfig.Config { + return projectconfig.Config{ + Path: proto.Dir, + Name: proto.Name, + NoGit: proto.NoGit, + Hermit: proto.Hermit, } } diff --git a/frontend/console/src/protos/xyz/block/ftl/v1/language/language_connect.ts b/frontend/console/src/protos/xyz/block/ftl/v1/language/language_connect.ts index eb4dfa2aec..e9c2e15394 100644 --- a/frontend/console/src/protos/xyz/block/ftl/v1/language/language_connect.ts +++ b/frontend/console/src/protos/xyz/block/ftl/v1/language/language_connect.ts @@ -5,7 +5,7 @@ import { PingRequest, PingResponse } from "../ftl_pb.js"; import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf"; -import { BuildContextUpdatedRequest, BuildContextUpdatedResponse, BuildEvent, BuildRequest, CreateModuleRequest, CreateModuleResponse, DependenciesRequest, DependenciesResponse, GetCreateModuleFlagsRequest, GetCreateModuleFlagsResponse, ModuleConfigDefaultsRequest, ModuleConfigDefaultsResponse } from "./language_pb.js"; +import { BuildContextUpdatedRequest, BuildContextUpdatedResponse, BuildEvent, BuildRequest, CreateModuleRequest, CreateModuleResponse, DependenciesRequest, DependenciesResponse, GenerateStubsRequest, GenerateStubsResponse, GetCreateModuleFlagsRequest, GetCreateModuleFlagsResponse, ModuleConfigDefaultsRequest, ModuleConfigDefaultsResponse, SyncStubReferencesRequest, SyncStubReferencesResponse } from "./language_pb.js"; /** * LanguageService allows a plugin to add support for a programming language. @@ -106,6 +106,40 @@ export const LanguageService = { O: BuildContextUpdatedResponse, kind: MethodKind.Unary, }, + /** + * Generate stubs for a module. + * + * Stubs allow modules to import other module's exported interface. If a language does not need this step, + * then it is not required to do anything in this call. + * + * This call is not tied to the module that this plugin is responsible for. A plugin of each language will + * be chosen to generate stubs for each module. + * + * @generated from rpc xyz.block.ftl.v1.language.LanguageService.GenerateStubs + */ + generateStubs: { + name: "GenerateStubs", + I: GenerateStubsRequest, + O: GenerateStubsResponse, + kind: MethodKind.Unary, + }, + /** + * SyncStubReferences is called when module stubs have been updated. This allows the plugin to update + * references to external modules, regardless of whether they are dependencies. + * + * For example, go plugin adds references to all modules into the go.work file so that tools can automatically + * import the modules when users start reference them. + * + * It is optional to do anything with this call. + * + * @generated from rpc xyz.block.ftl.v1.language.LanguageService.SyncStubReferences + */ + syncStubReferences: { + name: "SyncStubReferences", + I: SyncStubReferencesRequest, + O: SyncStubReferencesResponse, + kind: MethodKind.Unary, + }, } } as const; diff --git a/frontend/console/src/protos/xyz/block/ftl/v1/language/language_pb.ts b/frontend/console/src/protos/xyz/block/ftl/v1/language/language_pb.ts index 044a934dd0..2884736c6e 100644 --- a/frontend/console/src/protos/xyz/block/ftl/v1/language/language_pb.ts +++ b/frontend/console/src/protos/xyz/block/ftl/v1/language/language_pb.ts @@ -14,38 +14,51 @@ import { Module, Schema } from "../schema/schema_pb.js"; */ export class ModuleConfig extends Message { /** - * name of the module + * Name of the module * * @generated from field: string name = 1; */ name = ""; /** - * absolute path to the module's directory + * Absolute path to the module's directory * - * @generated from field: string path = 2; + * @generated from field: string dir = 2; */ - path = ""; + dir = ""; /** - * absolute path + * The language of the module * - * @generated from field: string deployDir = 3; + * @generated from field: string language = 3; + */ + language = ""; + + /** + * Absolute path to the directory containing all of this module's build artifacts for deployments + * + * @generated from field: string deploy_dir = 4; */ deployDir = ""; /** - * @generated from field: optional string build = 4; + * Build is the command to build the module. + * + * @generated from field: optional string build = 5; */ build?: string; /** - * @generated from field: optional string generated_schema_dir = 5; + * The directory to generate protobuf schema files into. These can be picked up by language specific build tools + * + * @generated from field: optional string generated_schema_dir = 6; */ generatedSchemaDir?: string; /** - * @generated from field: repeated string watch = 6; + * Patterns to watch for file changes + * + * @generated from field: repeated string watch = 7; */ watch: string[] = []; @@ -53,7 +66,7 @@ export class ModuleConfig extends Message { * LanguageConfig contains any metadata specific to a specific language. * These are stored in the ftl.toml file under the same key as the language (eg: "go", "java") * - * @generated from field: google.protobuf.Struct language_config = 7; + * @generated from field: google.protobuf.Struct language_config = 8; */ languageConfig?: Struct; @@ -66,12 +79,13 @@ export class ModuleConfig extends Message { static readonly typeName = "xyz.block.ftl.v1.language.ModuleConfig"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ { no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "path", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 3, name: "deployDir", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 4, name: "build", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 5, name: "generated_schema_dir", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 6, name: "watch", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, - { no: 7, name: "language_config", kind: "message", T: Struct }, + { no: 2, name: "dir", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "language", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 4, name: "deploy_dir", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 5, name: "build", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 6, name: "generated_schema_dir", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 7, name: "watch", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + { no: 8, name: "language_config", kind: "message", T: Struct }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): ModuleConfig { @@ -98,9 +112,9 @@ export class ModuleConfig extends Message { */ export class ProjectConfig extends Message { /** - * @generated from field: string path = 1; + * @generated from field: string dir = 1; */ - path = ""; + dir = ""; /** * @generated from field: string name = 2; @@ -125,7 +139,7 @@ export class ProjectConfig extends Message { static readonly runtime: typeof proto3 = proto3; static readonly typeName = "xyz.block.ftl.v1.language.ProjectConfig"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "path", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 1, name: "dir", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 3, name: "no_git", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, { no: 4, name: "hermit", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, @@ -303,12 +317,12 @@ export class CreateModuleRequest extends Message { name = ""; /** - * The root path for the module, which does not yet exist. + * The root directory for the module, which does not yet exist. * The plugin should create the directory. * - * @generated from field: string path = 2; + * @generated from field: string dir = 2; */ - path = ""; + dir = ""; /** * The project configuration @@ -333,7 +347,7 @@ export class CreateModuleRequest extends Message { static readonly typeName = "xyz.block.ftl.v1.language.CreateModuleRequest"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ { no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "path", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "dir", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 3, name: "project_config", kind: "message", T: ProjectConfig }, { no: 4, name: "Flags", kind: "message", T: Struct }, ]); @@ -393,9 +407,9 @@ export class CreateModuleResponse extends Message { */ export class ModuleConfigDefaultsRequest extends Message { /** - * @generated from field: string path = 1; + * @generated from field: string dir = 1; */ - path = ""; + dir = ""; constructor(data?: PartialMessage) { super(); @@ -405,7 +419,7 @@ export class ModuleConfigDefaultsRequest extends Message [ - { no: 1, name: "path", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 1, name: "dir", kind: "scalar", T: 9 /* ScalarType.STRING */ }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): ModuleConfigDefaultsRequest { @@ -440,7 +454,7 @@ export class ModuleConfigDefaultsResponse extends Message [ - { no: 1, name: "deployDir", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 1, name: "deploy_dir", kind: "scalar", T: 9 /* ScalarType.STRING */ }, { no: 2, name: "build", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 3, name: "generated_schema_dir", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 4, name: "watch", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, @@ -892,19 +906,26 @@ export class BuildRequest extends Message { /** * The root path for the FTL project * - * @generated from field: string project_path = 1; + * @generated from field: string project_root = 1; */ - projectPath = ""; + projectRoot = ""; + + /** + * The path to the directory containing all module stubs. Each module stub is in a subdirectory. + * + * @generated from field: string stubs_root = 2; + */ + stubsRoot = ""; /** * Indicates whether to watch for file changes and automatically rebuild * - * @generated from field: bool rebuild_automatically = 2; + * @generated from field: bool rebuild_automatically = 3; */ rebuildAutomatically = false; /** - * @generated from field: xyz.block.ftl.v1.language.BuildContext build_context = 3; + * @generated from field: xyz.block.ftl.v1.language.BuildContext build_context = 4; */ buildContext?: BuildContext; @@ -916,9 +937,10 @@ export class BuildRequest extends Message { static readonly runtime: typeof proto3 = proto3; static readonly typeName = "xyz.block.ftl.v1.language.BuildRequest"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "project_path", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "rebuild_automatically", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, - { no: 3, name: "build_context", kind: "message", T: BuildContext }, + { no: 1, name: "project_root", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "stubs_root", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "rebuild_automatically", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, + { no: 4, name: "build_context", kind: "message", T: BuildContext }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): BuildRequest { @@ -1279,3 +1301,183 @@ export class BuildEvent extends Message { } } +/** + * @generated from message xyz.block.ftl.v1.language.GenerateStubsRequest + */ +export class GenerateStubsRequest extends Message { + /** + * The directory path to generate stubs into + * + * @generated from field: string dir = 1; + */ + dir = ""; + + /** + * The schema of the module to generate stubs for + * + * @generated from field: xyz.block.ftl.v1.schema.Module module = 2; + */ + module?: Module; + + /** + * The module's configuration to generate stubs for + * + * @generated from field: xyz.block.ftl.v1.language.ModuleConfig module_config = 3; + */ + moduleConfig?: ModuleConfig; + + /** + * Native module configuration is the configuration for a module that uses the plugin's language, if + * the main moduleConfig provided is of a different language. It is provided as a mechanism to derive + * language specific information. For example, the language version. + * + * @generated from field: optional xyz.block.ftl.v1.language.ModuleConfig native_module_config = 4; + */ + nativeModuleConfig?: ModuleConfig; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.v1.language.GenerateStubsRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "dir", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "module", kind: "message", T: Module }, + { no: 3, name: "module_config", kind: "message", T: ModuleConfig }, + { no: 4, name: "native_module_config", kind: "message", T: ModuleConfig, opt: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): GenerateStubsRequest { + return new GenerateStubsRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): GenerateStubsRequest { + return new GenerateStubsRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): GenerateStubsRequest { + return new GenerateStubsRequest().fromJsonString(jsonString, options); + } + + static equals(a: GenerateStubsRequest | PlainMessage | undefined, b: GenerateStubsRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(GenerateStubsRequest, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.v1.language.GenerateStubsResponse + */ +export class GenerateStubsResponse extends Message { + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.v1.language.GenerateStubsResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): GenerateStubsResponse { + return new GenerateStubsResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): GenerateStubsResponse { + return new GenerateStubsResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): GenerateStubsResponse { + return new GenerateStubsResponse().fromJsonString(jsonString, options); + } + + static equals(a: GenerateStubsResponse | PlainMessage | undefined, b: GenerateStubsResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(GenerateStubsResponse, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.v1.language.SyncStubReferencesRequest + */ +export class SyncStubReferencesRequest extends Message { + /** + * @generated from field: xyz.block.ftl.v1.language.ModuleConfig module_config = 1; + */ + moduleConfig?: ModuleConfig; + + /** + * The path of the directory containing all module stubs. Each module is in a subdirectory + * + * @generated from field: string stubs_root = 2; + */ + stubsRoot = ""; + + /** + * The names of all modules that have had stubs generated + * + * @generated from field: repeated string modules = 3; + */ + modules: string[] = []; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.v1.language.SyncStubReferencesRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "module_config", kind: "message", T: ModuleConfig }, + { no: 2, name: "stubs_root", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "modules", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): SyncStubReferencesRequest { + return new SyncStubReferencesRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): SyncStubReferencesRequest { + return new SyncStubReferencesRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): SyncStubReferencesRequest { + return new SyncStubReferencesRequest().fromJsonString(jsonString, options); + } + + static equals(a: SyncStubReferencesRequest | PlainMessage | undefined, b: SyncStubReferencesRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(SyncStubReferencesRequest, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.v1.language.SyncStubReferencesResponse + */ +export class SyncStubReferencesResponse extends Message { + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.v1.language.SyncStubReferencesResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): SyncStubReferencesResponse { + return new SyncStubReferencesResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): SyncStubReferencesResponse { + return new SyncStubReferencesResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): SyncStubReferencesResponse { + return new SyncStubReferencesResponse().fromJsonString(jsonString, options); + } + + static equals(a: SyncStubReferencesResponse | PlainMessage | undefined, b: SyncStubReferencesResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(SyncStubReferencesResponse, a, b); + } +} + diff --git a/go-runtime/compile/build.go b/go-runtime/compile/build.go index 116b58fbfc..9811a2b10f 100644 --- a/go-runtime/compile/build.go +++ b/go-runtime/compile/build.go @@ -39,7 +39,6 @@ type MainWorkContext struct { } type ExternalModuleContext struct { - *schema.Schema GoVersion string FTLVersion string Module *schema.Module @@ -258,7 +257,7 @@ func buildDir(moduleDir string) string { } // Build the given module. -func Build(ctx context.Context, projectRootDir, moduleDir string, config moduleconfig.AbsModuleConfig, sch *schema.Schema, filesTransaction ModifyFilesTransaction, buildEnv []string, devMode bool) (moduleSch *schema.Module, buildErrors []builderrors.Error, err error) { +func Build(ctx context.Context, projectRootDir, stubsRoot string, config moduleconfig.AbsModuleConfig, sch *schema.Schema, filesTransaction ModifyFilesTransaction, buildEnv []string, devMode bool) (moduleSch *schema.Module, buildErrors []builderrors.Error, err error) { if err := filesTransaction.Begin(); err != nil { return nil, nil, fmt.Errorf("could not start a file transaction: %w", err) } @@ -268,14 +267,14 @@ func Build(ctx context.Context, projectRootDir, moduleDir string, config modulec } }() - replacements, goModVersion, err := updateGoModule(filepath.Join(moduleDir, "go.mod")) + replacements, goModVersion, err := updateGoModule(filepath.Join(config.Dir, "go.mod")) if err != nil { return nil, nil, err } goVersion := runtime.Version()[2:] if semver.Compare("v"+goVersion, "v"+goModVersion) < 0 { - return nil, nil, fmt.Errorf("go version %q is not recent enough for this module, needs minimum version %q", goVersion, goModVersion) + return moduleSch, nil, fmt.Errorf("go version %q is not recent enough for this module, needs minimum version %q", goVersion, goModVersion) } ftlVersion := "" @@ -287,7 +286,7 @@ func Build(ctx context.Context, projectRootDir, moduleDir string, config modulec if pcpath, ok := projectconfig.DefaultConfigPath().Get(); ok { pc, err := projectconfig.Load(ctx, pcpath) if err != nil { - return nil, nil, fmt.Errorf("failed to load project config: %w", err) + return moduleSch, nil, fmt.Errorf("failed to load project config: %w", err) } projectName = pc.Name } @@ -295,10 +294,10 @@ func Build(ctx context.Context, projectRootDir, moduleDir string, config modulec logger := log.FromContext(ctx) funcs := maps.Clone(scaffoldFuncs) - buildDir := buildDir(moduleDir) + buildDir := buildDir(config.Dir) err = os.MkdirAll(buildDir, 0750) if err != nil { - return nil, nil, fmt.Errorf("failed to create build directory: %w", err) + return moduleSch, nil, fmt.Errorf("failed to create build directory: %w", err) } var sharedModulesPaths []string @@ -306,37 +305,37 @@ func Build(ctx context.Context, projectRootDir, moduleDir string, config modulec if mod.Name == config.Module { continue } - sharedModulesPaths = append(sharedModulesPaths, filepath.Join(projectRootDir, buildDirName, "go", "modules", mod.Name)) + sharedModulesPaths = append(sharedModulesPaths, filepath.Join(stubsRoot, mod.Name)) } - if err := internal.ScaffoldZip(mainWorkTemplateFiles(), moduleDir, MainWorkContext{ + if err := internal.ScaffoldZip(mainWorkTemplateFiles(), config.Dir, MainWorkContext{ GoVersion: goModVersion, SharedModulesPaths: sharedModulesPaths, }, scaffolder.Exclude("^go.mod$"), scaffolder.Functions(funcs)); err != nil { - return nil, nil, fmt.Errorf("failed to scaffold zip: %w", err) + return moduleSch, nil, fmt.Errorf("failed to scaffold zip: %w", err) } logger.Debugf("Extracting schema") result, err := extract.Extract(config.Dir) if err != nil { - return nil, nil, fmt.Errorf("could not extract schema: %w", err) + return moduleSch, nil, fmt.Errorf("could not extract schema: %w", err) } if builderrors.ContainsTerminalError(result.Errors) { // Only bail if schema errors contain elements at level ERROR. // If errors are only at levels below ERROR (e.g. INFO, WARN), the schema can still be used. - return nil, result.Errors, nil + return moduleSch, result.Errors, nil } logger.Debugf("Generating main module") mctx, err := buildMainModuleContext(sch, result, goModVersion, ftlVersion, projectName, sharedModulesPaths, replacements) if err != nil { - return nil, nil, err + return moduleSch, nil, err } - if err := internal.ScaffoldZip(buildTemplateFiles(), moduleDir, mctx, scaffolder.Exclude("^go.mod$"), + if err := internal.ScaffoldZip(buildTemplateFiles(), config.Dir, mctx, scaffolder.Exclude("^go.mod$"), scaffolder.Functions(funcs)); err != nil { - return nil, nil, fmt.Errorf("failed to scaffold build template: %w", err) + return moduleSch, nil, fmt.Errorf("failed to scaffold build template: %w", err) } logger.Debugf("Tidying go.mod files") @@ -344,14 +343,14 @@ func Build(ctx context.Context, projectRootDir, moduleDir string, config modulec ftlTypesFilename := "types.ftl.go" wg.Go(func() error { - if err := exec.Command(wgctx, log.Debug, moduleDir, "go", "mod", "tidy").RunBuffered(wgctx); err != nil { - return fmt.Errorf("%s: failed to tidy go.mod: %w", moduleDir, err) + if err := exec.Command(wgctx, log.Debug, config.Dir, "go", "mod", "tidy").RunBuffered(wgctx); err != nil { + return fmt.Errorf("%s: failed to tidy go.mod: %w", config.Dir, err) } - if err := exec.Command(wgctx, log.Debug, moduleDir, "go", "fmt", ftlTypesFilename).RunBuffered(wgctx); err != nil { - return fmt.Errorf("%s: failed to format module dir: %w", moduleDir, err) + if err := exec.Command(wgctx, log.Debug, config.Dir, "go", "fmt", ftlTypesFilename).RunBuffered(wgctx); err != nil { + return fmt.Errorf("%s: failed to format module dir: %w", config.Dir, err) } - return filesTransaction.ModifiedFiles(filepath.Join(moduleDir, "go.mod"), filepath.Join(moduleDir, "go.sum"), filepath.Join(moduleDir, ftlTypesFilename)) + return filesTransaction.ModifiedFiles(filepath.Join(config.Dir, "go.mod"), filepath.Join(config.Dir, "go.sum"), filepath.Join(config.Dir, ftlTypesFilename)) }) mainDir := filepath.Join(buildDir, "go", "main") wg.Go(func() error { @@ -361,10 +360,10 @@ func Build(ctx context.Context, projectRootDir, moduleDir string, config modulec if err := exec.Command(wgctx, log.Debug, mainDir, "go", "fmt", "./...").RunBuffered(wgctx); err != nil { return fmt.Errorf("%s: failed to format main dir: %w", mainDir, err) } - return filesTransaction.ModifiedFiles(filepath.Join(mainDir, "go.mod"), filepath.Join(moduleDir, "go.sum")) + return filesTransaction.ModifiedFiles(filepath.Join(mainDir, "go.mod"), filepath.Join(config.Dir, "go.sum")) }) if err := wg.Wait(); err != nil { - return nil, nil, err // nolint:wrapcheck + return moduleSch, nil, err // nolint:wrapcheck } logger.Debugf("Compiling") @@ -377,7 +376,7 @@ func Build(ctx context.Context, projectRootDir, moduleDir string, config modulec buildEnv = append(buildEnv, "GODEBUG=http2client=0") err = exec.CommandWithEnv(ctx, log.Debug, mainDir, buildEnv, "go", args...).RunBuffered(ctx) if err != nil { - return nil, nil, fmt.Errorf("failed to compile: %w", err) + return moduleSch, nil, fmt.Errorf("failed to compile: %w", err) } err = os.WriteFile(filepath.Join(mainDir, "../../launch"), []byte(`#!/bin/bash if [ -n "$FTL_DEBUG_PORT" ] && command -v dlv &> /dev/null ; then @@ -387,145 +386,11 @@ func Build(ctx context.Context, projectRootDir, moduleDir string, config modulec fi `), 0770) // #nosec if err != nil { - return nil, nil, fmt.Errorf("failed to write launch script: %w", err) + return moduleSch, nil, fmt.Errorf("failed to write launch script: %w", err) } return result.Module, result.Errors, nil } -// CleanStubs removes all generated stubs. -func CleanStubs(ctx context.Context, projectRoot string) error { - logger := log.FromContext(ctx) - logger.Debugf("Deleting all generated stubs") - sharedFtlDir := filepath.Join(projectRoot, buildDirName) - - // Wipe the modules directory to ensure we don't have any stale modules. - err := os.RemoveAll(sharedFtlDir) - if err != nil { - return fmt.Errorf("failed to remove %s: %w", sharedFtlDir, err) - } - - return nil -} - -// GenerateStubsForModules generates stubs for all modules in the schema. -func GenerateStubsForModules(ctx context.Context, projectRoot string, moduleConfigs []moduleconfig.ModuleConfig, sch *schema.Schema) error { - logger := log.FromContext(ctx) - logger.Debugf("Generating go module stubs") - - sharedFtlDir := filepath.Join(projectRoot, buildDirName) - - ftlVersion := "" - if ftl.IsRelease(ftl.Version) { - ftlVersion = ftl.Version - } - hasGo := false - for _, mc := range moduleConfigs { - if mc.Language == "go" && mc.Module != "builtin" { - hasGo = true - } - } - if !hasGo { - return nil - } - - for _, module := range sch.Modules { - var moduleConfig *moduleconfig.ModuleConfig - for _, mc := range moduleConfigs { - mcCopy := mc - if mc.Module == module.Name && mc.Language == "go" { - moduleConfig = &mcCopy - break - } - } - - var goModVersion string - var replacements []*modfile.Replace - var err error - - // If there's no module config, use the go.mod file for the first config we find. - if moduleConfig == nil { - for _, mod := range moduleConfigs { - if mod.Language != "go" { - continue - } - goModPath := filepath.Join(mod.Dir, "go.mod") - _, goModVersion, err = updateGoModule(goModPath) - if err != nil { - logger.Debugf("could not read go.mod %s", goModPath) - continue - } - } - if goModVersion == "" { - // The best we can do here if we don't have a module to read from is to use the current Go version. - goModVersion = runtime.Version()[2:] - } - - replacements = []*modfile.Replace{} - } else { - replacements, goModVersion, err = updateGoModule(filepath.Join(moduleConfig.Dir, "go.mod")) - if err != nil { - return err - } - } - - goVersion := runtime.Version()[2:] - if semver.Compare("v"+goVersion, "v"+goModVersion) < 0 { - return fmt.Errorf("go version %q is not recent enough for this module, needs minimum version %q", goVersion, goModVersion) - } - - context := ExternalModuleContext{ - Schema: sch, - GoVersion: goModVersion, - FTLVersion: ftlVersion, - Module: module, - Replacements: replacements, - } - - funcs := maps.Clone(scaffoldFuncs) - err = internal.ScaffoldZip(externalModuleTemplateFiles(), projectRoot, context, scaffolder.Exclude("^go.mod$"), scaffolder.Functions(funcs)) - if err != nil { - return fmt.Errorf("failed to scaffold zip: %w", err) - } - - modulesDir := filepath.Join(sharedFtlDir, "go", "modules", module.Name) - if err := exec.Command(ctx, log.Debug, modulesDir, "go", "mod", "tidy").RunBuffered(ctx); err != nil { - return fmt.Errorf("failed to tidy go.mod: %w", err) - } - } - - return nil -} - -func SyncGeneratedStubReferences(ctx context.Context, projectRootDir string, stubbedModules []string, moduleConfigs []moduleconfig.ModuleConfig) error { - for _, moduleConfig := range moduleConfigs { - var sharedModulesPaths []string - for _, mod := range stubbedModules { - if mod == moduleConfig.Module { - continue - } - sharedModulesPaths = append(sharedModulesPaths, filepath.Join(projectRootDir, buildDirName, "go", "modules", mod)) - } - if moduleConfig.Language != "go" { - continue - } - - _, goModVersion, err := updateGoModule(filepath.Join(moduleConfig.Dir, "go.mod")) - if err != nil { - return err - } - - funcs := maps.Clone(scaffoldFuncs) - if err := internal.ScaffoldZip(mainWorkTemplateFiles(), moduleConfig.Dir, MainWorkContext{ - GoVersion: goModVersion, - SharedModulesPaths: sharedModulesPaths, - }, scaffolder.Exclude("^go.mod$"), scaffolder.Functions(funcs)); err != nil { - return fmt.Errorf("failed to scaffold zip: %w", err) - } - } - - return nil -} - type mainModuleContextBuilder struct { sch *schema.Schema mainModule *schema.Module diff --git a/go-runtime/compile/dependencies.go b/go-runtime/compile/dependencies.go new file mode 100644 index 0000000000..6a11c10295 --- /dev/null +++ b/go-runtime/compile/dependencies.go @@ -0,0 +1,58 @@ +package compile + +import ( + "fmt" + "go/parser" + "go/token" + "io/fs" + "sort" + "strconv" + "strings" + + "golang.org/x/exp/maps" + + "github.com/TBD54566975/ftl/internal/moduleconfig" + "github.com/TBD54566975/ftl/internal/watch" +) + +func ExtractDependencies(config moduleconfig.AbsModuleConfig) ([]string, error) { + dependencies := map[string]bool{} + fset := token.NewFileSet() + err := watch.WalkDir(config.Dir, func(path string, d fs.DirEntry) error { + if !d.IsDir() { + return nil + } + if strings.HasPrefix(d.Name(), "_") || d.Name() == "testdata" { + return watch.ErrSkip + } + pkgs, err := parser.ParseDir(fset, path, nil, parser.ImportsOnly) + if pkgs == nil { + return fmt.Errorf("could parse directory in search of dependencies: %w", err) + } + for _, pkg := range pkgs { + for _, file := range pkg.Files { + for _, imp := range file.Imports { + path, err := strconv.Unquote(imp.Path.Value) + if err != nil { + continue + } + if !strings.HasPrefix(path, "ftl/") { + continue + } + module := strings.Split(strings.TrimPrefix(path, "ftl/"), "/")[0] + if module == config.Module { + continue + } + dependencies[module] = true + } + } + } + return nil + }) + if err != nil { + return nil, fmt.Errorf("%s: failed to extract dependencies from Go module: %w", config.Module, err) + } + modules := maps.Keys(dependencies) + sort.Strings(modules) + return modules, nil +} diff --git a/go-runtime/compile/external-module-template/.ftl/go/modules/{{ .Module.Name }}/external_module.go.tmpl b/go-runtime/compile/external-module-template/external_module.go.tmpl similarity index 100% rename from go-runtime/compile/external-module-template/.ftl/go/modules/{{ .Module.Name }}/external_module.go.tmpl rename to go-runtime/compile/external-module-template/external_module.go.tmpl diff --git a/go-runtime/compile/external-module-template/.ftl/go/modules/{{ .Module.Name }}/go.mod.tmpl b/go-runtime/compile/external-module-template/go.mod.tmpl similarity index 100% rename from go-runtime/compile/external-module-template/.ftl/go/modules/{{ .Module.Name }}/go.mod.tmpl rename to go-runtime/compile/external-module-template/go.mod.tmpl diff --git a/go-runtime/compile/stubs.go b/go-runtime/compile/stubs.go new file mode 100644 index 0000000000..6f2104fb9c --- /dev/null +++ b/go-runtime/compile/stubs.go @@ -0,0 +1,102 @@ +package compile + +import ( + "context" + "fmt" + "path/filepath" + "runtime" + + "github.com/TBD54566975/scaffolder" + "github.com/alecthomas/types/optional" + "golang.org/x/exp/maps" + "golang.org/x/mod/modfile" + "golang.org/x/mod/semver" + + "github.com/TBD54566975/ftl" + "github.com/TBD54566975/ftl/internal" + "github.com/TBD54566975/ftl/internal/exec" + "github.com/TBD54566975/ftl/internal/log" + "github.com/TBD54566975/ftl/internal/moduleconfig" + "github.com/TBD54566975/ftl/internal/schema" +) + +func GenerateStubs(ctx context.Context, dir string, moduleSch *schema.Module, config moduleconfig.AbsModuleConfig, nativeConfig optional.Option[moduleconfig.AbsModuleConfig]) error { + var goModVersion string + var replacements []*modfile.Replace + var err error + + // If there's no module config, use the go.mod file for the first config we find. + if config.Module == "builtin" || config.Language != "go" { + nativeConfig, ok := nativeConfig.Get() + if !ok { + return fmt.Errorf("no native module config provided") + } + goModPath := filepath.Join(nativeConfig.Dir, "go.mod") + _, goModVersion, err = updateGoModule(goModPath) + if err != nil { + return fmt.Errorf("could not read go.mod %s", goModPath) + } + if goModVersion == "" { + // The best we can do here if we don't have a module to read from is to use the current Go version. + goModVersion = runtime.Version()[2:] + } + replacements = []*modfile.Replace{} + } else { + replacements, goModVersion, err = updateGoModule(filepath.Join(config.Dir, "go.mod")) + if err != nil { + return err + } + } + + goVersion := runtime.Version()[2:] + if semver.Compare("v"+goVersion, "v"+goModVersion) < 0 { + return fmt.Errorf("go version %q is not recent enough for this module, needs minimum version %q", goVersion, goModVersion) + } + + ftlVersion := "" + if ftl.IsRelease(ftl.Version) { + ftlVersion = ftl.Version + } + + context := ExternalModuleContext{ + GoVersion: goModVersion, + FTLVersion: ftlVersion, + Module: moduleSch, + Replacements: replacements, + } + + funcs := maps.Clone(scaffoldFuncs) + err = internal.ScaffoldZip(externalModuleTemplateFiles(), dir, context, scaffolder.Exclude("^go.mod$"), scaffolder.Functions(funcs)) + if err != nil { + return fmt.Errorf("failed to scaffold zip: %w", err) + } + + if err := exec.Command(ctx, log.Debug, dir, "go", "mod", "tidy").RunBuffered(ctx); err != nil { + return fmt.Errorf("failed to tidy go.mod: %w", err) + } + return nil +} + +func SyncGeneratedStubReferences(ctx context.Context, config moduleconfig.ModuleConfig, stubsDir string, stubbedModules []string) error { + sharedModulePaths := []string{} + for _, mod := range stubbedModules { + if mod == config.Module { + continue + } + sharedModulePaths = append(sharedModulePaths, filepath.Join(stubsDir, mod)) + } + + _, goModVersion, err := updateGoModule(filepath.Join(config.Abs().Dir, "go.mod")) + if err != nil { + return err + } + + funcs := maps.Clone(scaffoldFuncs) + if err := internal.ScaffoldZip(mainWorkTemplateFiles(), config.Abs().Dir, MainWorkContext{ + GoVersion: goModVersion, + SharedModulesPaths: sharedModulePaths, + }, scaffolder.Exclude("^go.mod$"), scaffolder.Functions(funcs)); err != nil { + return fmt.Errorf("failed to scaffold zip: %w", err) + } + return nil +} diff --git a/internal/buildengine/stubs_test.go b/go-runtime/compile/stubs_test.go similarity index 72% rename from internal/buildengine/stubs_test.go rename to go-runtime/compile/stubs_test.go index af43ec9a31..4eaeb3ed29 100644 --- a/internal/buildengine/stubs_test.go +++ b/go-runtime/compile/stubs_test.go @@ -1,18 +1,42 @@ -package buildengine +package compile import ( "context" + "fmt" "os" "path/filepath" "testing" "github.com/alecthomas/assert/v2" + "github.com/alecthomas/types/optional" "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/moduleconfig" "github.com/TBD54566975/ftl/internal/schema" ) +func setUp(t *testing.T) (ctx context.Context, projectRoot, fakeGoModDir string) { + t.Helper() + + ctx = log.ContextWithNewDefaultLogger(context.Background()) + + projectRoot = t.TempDir() + fakeDir := filepath.Join(projectRoot, "fakegomod") + err := os.MkdirAll(fakeDir, 0700) + assert.NoError(t, err) + ftlPath, err := filepath.Abs("../..") + assert.NoError(t, err) + err = os.WriteFile(filepath.Join(fakeDir, "go.mod"), []byte(fmt.Sprintf(` + module ftl/fake + go 1.23.0 + + replace github.com/TBD54566975/ftl => %s + `, ftlPath)), 0600) + // err := copy.Copy(filepath.Join("testdata", "go", "time"), filepath.Join(projectRoot, "time")) + assert.NoError(t, err) + return ctx, projectRoot, fakeDir +} + func TestGenerateGoStubs(t *testing.T) { if testing.Short() { t.SkipNow() @@ -170,11 +194,21 @@ func init() { ) } ` - - ctx := log.ContextWithNewDefaultLogger(context.Background()) - projectRoot := t.TempDir() - err := GenerateStubs(ctx, projectRoot, modules, []moduleconfig.ModuleConfig{{Language: "go"}}) - assert.NoError(t, err) + ctx, projectRoot, fakeDir := setUp(t) + for _, module := range modules { + path := filepath.Join(projectRoot, ".ftl", "go", "modules", module.Name) + err := os.MkdirAll(path, 0700) + assert.NoError(t, err) + + // Generate stubs needs a go.mod file to check for go version + // Force each of these test modules to point to time's dir just so it can read the go.mod file + config := moduleconfig.AbsModuleConfig{ + Language: "go", + Dir: fakeDir, + } + err = GenerateStubs(ctx, path, module, config, optional.None[moduleconfig.AbsModuleConfig]()) + assert.NoError(t, err) + } generatedPath := filepath.Join(projectRoot, ".ftl/go/modules/other/external_module.go") fileContent, err := os.ReadFile(generatedPath) @@ -229,10 +263,23 @@ type Resp struct { //ftl:verb type CallClient func(context.Context, Req) (Resp, error) ` - ctx := log.ContextWithNewDefaultLogger(context.Background()) - projectRoot := t.TempDir() - err := GenerateStubs(ctx, projectRoot, modules, []moduleconfig.ModuleConfig{{Language: "go"}}) - assert.NoError(t, err) + + ctx, projectRoot, fakeDir := setUp(t) + + for _, module := range modules { + path := filepath.Join(projectRoot, ".ftl", "go", "modules", module.Name) + err := os.MkdirAll(path, 0700) + assert.NoError(t, err) + + // Generate stubs needs a go.mod file to check for go version + // Force each of these test modules to point to time's dir just so it can read the go.mod file + config := moduleconfig.AbsModuleConfig{ + Language: "go", + Dir: fakeDir, + } + err = GenerateStubs(ctx, path, module, config, optional.None[moduleconfig.AbsModuleConfig]()) + assert.NoError(t, err) + } generatedPath := filepath.Join(projectRoot, ".ftl/go/modules/test/external_module.go") fileContent, err := os.ReadFile(generatedPath) diff --git a/internal/buildengine/build.go b/internal/buildengine/build.go index 782563ee72..6aa59e521a 100644 --- a/internal/buildengine/build.go +++ b/internal/buildengine/build.go @@ -29,7 +29,8 @@ func build(ctx context.Context, plugin languageplugin.LanguagePlugin, projectRoo ctx = log.ContextWithLogger(ctx, logger) logger.Infof("Building module") - return handleBuildResult(ctx, bctx.Config, result.From(plugin.Build(ctx, projectRootDir, bctx, buildEnv, devMode))) + stubsRoot := stubsLanguageDir(projectRootDir, bctx.Config.Language) + return handleBuildResult(ctx, bctx.Config, result.From(plugin.Build(ctx, projectRootDir, stubsRoot, bctx, buildEnv, devMode))) } // handleBuildResult processes the result of a build diff --git a/internal/buildengine/engine.go b/internal/buildengine/engine.go index 5a69481d76..e21cd94532 100644 --- a/internal/buildengine/engine.go +++ b/internal/buildengine/engine.go @@ -53,16 +53,6 @@ func copyMetaWithUpdatedDependencies(ctx context.Context, m moduleMeta) (moduleM if err != nil { return moduleMeta{}, fmt.Errorf("could not get dependencies for %v: %w", m.module.Config.Module, err) } - containsBuiltin := false - for _, dep := range dependencies { - if dep == "builtin" { - containsBuiltin = true - break - } - } - if !containsBuiltin { - dependencies = append(dependencies, "builtin") - } m.module = m.module.CopyWithDependencies(dependencies) return m, nil @@ -275,7 +265,7 @@ func (e *Engine) buildGraph(moduleName string, out map[string][]string) error { foundModule := false if meta, ok := e.moduleMetas.Load(moduleName); ok { foundModule = true - deps = meta.module.Dependencies + deps = meta.module.Dependencies(AlwaysIncludeBuiltin) } if !foundModule { if sch, ok := e.controllerSchema.Load(moduleName); ok { @@ -580,7 +570,7 @@ func computeModuleHash(module *schema.Module) ([]byte, error) { func (e *Engine) getDependentModuleNames(moduleName string) []string { dependentModuleNames := map[string]bool{} e.moduleMetas.Range(func(name string, meta moduleMeta) bool { - for _, dep := range meta.module.Dependencies { + for _, dep := range meta.module.Dependencies(AlwaysIncludeBuiltin) { if dep == moduleName { dependentModuleNames[name] = true } @@ -690,12 +680,12 @@ func (e *Engine) buildWithCallback(ctx context.Context, callback buildCallback, return err } - metas := e.allModuleMetas() - moduleConfigs := make([]moduleconfig.ModuleConfig, len(metas)) - for i, meta := range metas { - moduleConfigs[i] = meta.module.Config - } - err = GenerateStubs(ctx, e.projectRoot, maps.Values(knownSchemas), moduleConfigs) + metasMap := map[string]moduleMeta{} + e.moduleMetas.Range(func(name string, meta moduleMeta) bool { + metasMap[name] = meta + return true + }) + err = GenerateStubs(ctx, e.projectRoot, maps.Values(knownSchemas), metasMap) if err != nil { return err } @@ -735,7 +725,7 @@ func (e *Engine) buildWithCallback(ctx context.Context, callback buildCallback, } // Sync references to stubs if needed by the runtime - err = SyncStubReferences(ctx, e.projectRoot, moduleNames, moduleConfigs) + err = SyncStubReferences(ctx, e.projectRoot, moduleNames, metasMap) if err != nil { return err } @@ -766,7 +756,7 @@ func (e *Engine) tryBuild(ctx context.Context, mustBuild map[string]bool, module return fmt.Errorf("module %q not found", moduleName) } - for _, dep := range meta.module.Dependencies { + for _, dep := range meta.module.Dependencies(Raw) { if _, ok := builtModules[dep]; !ok { logger.Warnf("build skipped because dependency %q failed to build", dep) return nil @@ -815,7 +805,7 @@ func (e *Engine) build(ctx context.Context, moduleName string, builtModules map[ moduleSchema, deploy, err := build(ctx, meta.plugin, e.projectRoot, languageplugin.BuildContext{ Config: meta.module.Config, Schema: sch, - Dependencies: meta.module.Dependencies, + Dependencies: meta.module.Dependencies(Raw), }, e.buildEnv, e.devMode) if err != nil { terminal.UpdateModuleState(ctx, moduleName, terminal.BuildStateFailed) @@ -836,15 +826,6 @@ func (e *Engine) build(ctx context.Context, moduleName string, builtModules map[ return nil } -func (e *Engine) allModuleMetas() []moduleMeta { - var out []moduleMeta - e.moduleMetas.Range(func(name string, meta moduleMeta) bool { - out = append(out, meta) - return true - }) - return out -} - // Construct a combined schema for a module and its transitive dependencies. func (e *Engine) gatherSchemas( moduleSchemas map[string]*schema.Module, @@ -903,10 +884,7 @@ func (e *Engine) newModuleMeta(ctx context.Context, config moduleconfig.Unvalida return moduleMeta{}, fmt.Errorf("could not apply defaults for %s: %w", config.Module, err) } return moduleMeta{ - module: Module{ - Config: validConfig, - Dependencies: []string{}, - }, + module: newModule(validConfig), plugin: plugin, events: events, configDefaults: customDefaults, diff --git a/internal/buildengine/languageplugin/external_plugin.go b/internal/buildengine/languageplugin/external_plugin.go index adaa3edfae..5cde043ad6 100644 --- a/internal/buildengine/languageplugin/external_plugin.go +++ b/internal/buildengine/languageplugin/external_plugin.go @@ -28,9 +28,11 @@ const launchTimeout = 10 * time.Second type externalBuildCommand struct { BuildContext projectRoot string + stubsRoot string rebuildAutomatically bool - result chan result.Result[BuildResult] + startTime time.Time + result chan result.Result[BuildResult] } type externalPlugin struct { @@ -125,15 +127,19 @@ func (p *externalPlugin) GetCreateModuleFlags(ctx context.Context) ([]*kong.Flag // CreateModule creates a new module in the given directory with the given name and language. func (p *externalPlugin) CreateModule(ctx context.Context, projConfig projectconfig.Config, moduleConfig moduleconfig.ModuleConfig, flags map[string]string) error { - _, err := p.client.createModule(ctx, connect.NewRequest(&langpb.CreateModuleRequest{ - Name: moduleConfig.Module, - Path: moduleConfig.Dir, - ProjectConfig: &langpb.ProjectConfig{ - Path: projConfig.Path, - Name: projConfig.Name, - NoGit: projConfig.NoGit, - Hermit: projConfig.Hermit, - }, + genericFlags := map[string]any{} + for k, v := range flags { + genericFlags[k] = v + } + flagsProto, err := structpb.NewStruct(genericFlags) + if err != nil { + return fmt.Errorf("failed to convert flags to proto: %w", err) + } + _, err = p.client.createModule(ctx, connect.NewRequest(&langpb.CreateModuleRequest{ + Name: moduleConfig.Module, + Dir: moduleConfig.Dir, + ProjectConfig: langpb.ProjectConfigToProto(projConfig), + Flags: flagsProto, })) if err != nil { return fmt.Errorf("failed to create module: %w", err) @@ -143,22 +149,26 @@ func (p *externalPlugin) CreateModule(ctx context.Context, projConfig projectcon func (p *externalPlugin) ModuleConfigDefaults(ctx context.Context, dir string) (moduleconfig.CustomDefaults, error) { resp, err := p.client.moduleConfigDefaults(ctx, connect.NewRequest(&langpb.ModuleConfigDefaultsRequest{ - Path: dir, + Dir: dir, })) if err != nil { return moduleconfig.CustomDefaults{}, fmt.Errorf("failed to get module config defaults from plugin: %w", err) } + return customDefaultsFromProto(resp.Msg), nil +} + +func customDefaultsFromProto(proto *langpb.ModuleConfigDefaultsResponse) moduleconfig.CustomDefaults { return moduleconfig.CustomDefaults{ - DeployDir: resp.Msg.DeployDir, - Watch: resp.Msg.Watch, - Build: optional.Ptr(resp.Msg.Build), - GeneratedSchemaDir: optional.Ptr(resp.Msg.GeneratedSchemaDir), - LanguageConfig: resp.Msg.LanguageConfig.AsMap(), - }, nil + DeployDir: proto.DeployDir, + Watch: proto.Watch, + Build: optional.Ptr(proto.Build), + GeneratedSchemaDir: optional.Ptr(proto.GeneratedSchemaDir), + LanguageConfig: proto.LanguageConfig.AsMap(), + } } func (p *externalPlugin) GetDependencies(ctx context.Context, config moduleconfig.ModuleConfig) ([]string, error) { - configProto, err := protoFromModuleConfig(config) + configProto, err := langpb.ModuleConfigToProto(config.Abs()) if err != nil { return nil, err } @@ -171,12 +181,55 @@ func (p *externalPlugin) GetDependencies(ctx context.Context, config moduleconfi return resp.Msg.Modules, nil } +func (p *externalPlugin) GenerateStubs(ctx context.Context, dir string, module *schema.Module, moduleConfig moduleconfig.ModuleConfig, nativeModuleConfig optional.Option[moduleconfig.ModuleConfig]) error { + moduleProto := module.ToProto().(*schemapb.Module) //nolint:forcetypeassert + configProto, err := langpb.ModuleConfigToProto(moduleConfig.Abs()) + if err != nil { + return fmt.Errorf("could not create proto for module config: %w", err) + } + var nativeConfigProto *langpb.ModuleConfig + if config, ok := nativeModuleConfig.Get(); ok { + nativeConfigProto, err = langpb.ModuleConfigToProto(config.Abs()) + if err != nil { + return fmt.Errorf("could not create proto for native module config: %w", err) + } + } + _, err = p.client.generateStubs(ctx, connect.NewRequest(&langpb.GenerateStubsRequest{ + Dir: dir, + Module: moduleProto, + ModuleConfig: configProto, + NativeModuleConfig: nativeConfigProto, + })) + if err != nil { + return fmt.Errorf("plugin failed to generate stubs: %w", err) + } + return nil +} + +func (p *externalPlugin) SyncStubReferences(ctx context.Context, config moduleconfig.ModuleConfig, dir string, moduleNames []string) error { + configProto, err := langpb.ModuleConfigToProto(config.Abs()) + if err != nil { + return fmt.Errorf("could not create proto for native module config: %w", err) + } + _, err = p.client.syncStubReferences(ctx, connect.NewRequest(&langpb.SyncStubReferencesRequest{ + StubsRoot: dir, + Modules: moduleNames, + ModuleConfig: configProto, + })) + if err != nil { + return fmt.Errorf("plugin failed to sync stub references: %w", err) + } + return nil +} + // Build may result in a Build or BuildContextUpdated grpc call with the plugin, depending if a build stream is already set up -func (p *externalPlugin) Build(ctx context.Context, projectRoot string, bctx BuildContext, buildEnv []string, rebuildAutomatically bool) (BuildResult, error) { +func (p *externalPlugin) Build(ctx context.Context, projectRoot, stubsRoot string, bctx BuildContext, buildEnv []string, rebuildAutomatically bool) (BuildResult, error) { cmd := externalBuildCommand{ BuildContext: bctx, projectRoot: projectRoot, + stubsRoot: stubsRoot, rebuildAutomatically: rebuildAutomatically, + startTime: time.Now(), result: make(chan result.Result[BuildResult]), } p.commands <- cmd @@ -186,40 +239,18 @@ func (p *externalPlugin) Build(ctx context.Context, projectRoot string, bctx Bui if err != nil { return BuildResult{}, err //nolint:wrapcheck } + result.StartTime = cmd.startTime return result, nil case <-ctx.Done(): return BuildResult{}, fmt.Errorf("error waiting for build to complete: %w", ctx.Err()) } } -func protoFromModuleConfig(c moduleconfig.ModuleConfig) (*langpb.ModuleConfig, error) { - config := c.Abs() - proto := &langpb.ModuleConfig{ - Name: config.Module, - Path: config.Dir, - DeployDir: config.DeployDir, - Watch: config.Watch, - } - if config.Build != "" { - proto.Build = &config.Build - } - if config.GeneratedSchemaDir != "" { - proto.GeneratedSchemaDir = &config.GeneratedSchemaDir - } - - langConfigProto, err := structpb.NewStruct(config.LanguageConfig) - if err != nil { - return nil, fmt.Errorf("failed to marshal language config: %w", err) - } - proto.LanguageConfig = langConfigProto - - return proto, nil -} - func (p *externalPlugin) run(ctx context.Context) { // State var bctx BuildContext var projectRoot string + var stubsRoot string // if a current build stream is active, this is non-nil // this does not indicate if the stream is listening to automatic rebuilds @@ -244,6 +275,7 @@ func (p *externalPlugin) run(ctx context.Context) { contextCounter++ bctx = c.BuildContext projectRoot = c.projectRoot + stubsRoot = c.stubsRoot // module name may have changed, update logger scope logger = log.FromContext(ctx).Scope(bctx.Config.Module) @@ -252,7 +284,7 @@ func (p *externalPlugin) run(ctx context.Context) { c.result <- result.Err[BuildResult](fmt.Errorf("build already in progress")) continue } - configProto, err := protoFromModuleConfig(bctx.Config) + configProto, err := langpb.ModuleConfigToProto(bctx.Config.Abs()) if err != nil { c.result <- result.Err[BuildResult](err) continue @@ -279,7 +311,8 @@ func (p *externalPlugin) run(ctx context.Context) { } newStreamChan, newCancelFunc, err := p.client.build(ctx, connect.NewRequest(&langpb.BuildRequest{ - ProjectPath: projectRoot, + ProjectRoot: projectRoot, + StubsRoot: stubsRoot, RebuildAutomatically: c.rebuildAutomatically, BuildContext: &langpb.BuildContext{ Id: contextID(bctx.Config, contextCounter), @@ -394,13 +427,10 @@ func buildResultFromProto(result either.Either[*langpb.BuildEvent_BuildSuccess, switch result := result.(type) { case either.Left[*langpb.BuildEvent_BuildSuccess, *langpb.BuildEvent_BuildFailure]: buildSuccess := result.Get().BuildSuccess - var moduleSch *schema.Module - if buildSuccess.Module != nil { - sch, err := schema.ModuleFromProto(buildSuccess.Module) - if err != nil { - return BuildResult{}, fmt.Errorf("failed to parse schema: %w", err) - } - moduleSch = sch + + moduleSch, err := schema.ModuleFromProto(buildSuccess.Module) + if err != nil { + return BuildResult{}, fmt.Errorf("failed to parse schema: %w", err) } errs := langpb.ErrorsFromProto(buildSuccess.Errors) @@ -408,6 +438,7 @@ func buildResultFromProto(result either.Either[*langpb.BuildEvent_BuildSuccess, return BuildResult{ Errors: errs, Schema: moduleSch, + Deploy: buildSuccess.Deploy, }, nil case either.Right[*langpb.BuildEvent_BuildSuccess, *langpb.BuildEvent_BuildFailure]: buildFailure := result.Get().BuildFailure diff --git a/internal/buildengine/languageplugin/external_plugin_client.go b/internal/buildengine/languageplugin/external_plugin_client.go index 7611901c74..4b562cbd2c 100644 --- a/internal/buildengine/languageplugin/external_plugin_client.go +++ b/internal/buildengine/languageplugin/external_plugin_client.go @@ -25,6 +25,9 @@ type externalPluginClient interface { moduleConfigDefaults(ctx context.Context, req *connect.Request[langpb.ModuleConfigDefaultsRequest]) (*connect.Response[langpb.ModuleConfigDefaultsResponse], error) getDependencies(ctx context.Context, req *connect.Request[langpb.DependenciesRequest]) (*connect.Response[langpb.DependenciesResponse], error) + generateStubs(ctx context.Context, req *connect.Request[langpb.GenerateStubsRequest]) (*connect.Response[langpb.GenerateStubsResponse], error) + syncStubReferences(ctx context.Context, req *connect.Request[langpb.SyncStubReferencesRequest]) (*connect.Response[langpb.SyncStubReferencesResponse], error) + build(ctx context.Context, req *connect.Request[langpb.BuildRequest]) (chan result.Result[*langpb.BuildEvent], streamCancelFunc, error) buildContextUpdated(ctx context.Context, req *connect.Request[langpb.BuildContextUpdatedRequest]) (*connect.Response[langpb.BuildContextUpdatedResponse], error) @@ -44,7 +47,7 @@ func newExternalPluginImpl(ctx context.Context, bind *url.URL, language string) } err := impl.start(ctx, bind, language) if err != nil { - return nil, fmt.Errorf("failed to start plugin: %w", err) + return nil, err } return impl, nil } @@ -84,15 +87,15 @@ func (p *externalPluginImpl) start(ctx context.Context, bind *url.URL, language // Wait for ping result, or for the plugin to exit. Which ever happens first. select { case err := <-cmdErr: - if err == nil { - return fmt.Errorf("plugin exited with status 0 before ping was registered") + if err != nil { + return err } - return err + return fmt.Errorf("plugin exited with status 0 before ping was registered") case err := <-pingErr: if err != nil { - return nil + return fmt.Errorf("failed to start plugin: %w", err) } - return fmt.Errorf("failed to start plugin: %w", err) + return nil case <-ctx.Done(): return fmt.Errorf("failed to start plugin: %w", ctx.Err()) } @@ -148,6 +151,22 @@ func (p *externalPluginImpl) getDependencies(ctx context.Context, req *connect.R return resp, nil } +func (p *externalPluginImpl) generateStubs(ctx context.Context, req *connect.Request[langpb.GenerateStubsRequest]) (*connect.Response[langpb.GenerateStubsResponse], error) { + resp, err := p.client.GenerateStubs(ctx, req) + if err != nil { + return nil, err //nolint:wrapcheck + } + return resp, nil +} + +func (p *externalPluginImpl) syncStubReferences(ctx context.Context, req *connect.Request[langpb.SyncStubReferencesRequest]) (*connect.Response[langpb.SyncStubReferencesResponse], error) { + resp, err := p.client.SyncStubReferences(ctx, req) + if err != nil { + return nil, err //nolint:wrapcheck + } + return resp, nil +} + func (p *externalPluginImpl) build(ctx context.Context, req *connect.Request[langpb.BuildRequest]) (chan result.Result[*langpb.BuildEvent], streamCancelFunc, error) { stream, err := p.client.Build(ctx, req) if err != nil { @@ -158,8 +177,8 @@ func (p *externalPluginImpl) build(ctx context.Context, req *connect.Request[lan go streamToChan(stream, streamChan) return streamChan, func() { + // closing the stream causes the steamToChan goroutine to close the chan stream.Close() - close(streamChan) }, nil } diff --git a/internal/buildengine/languageplugin/external_plugin_test.go b/internal/buildengine/languageplugin/external_plugin_test.go index df4e132931..c2317483f6 100644 --- a/internal/buildengine/languageplugin/external_plugin_test.go +++ b/internal/buildengine/languageplugin/external_plugin_test.go @@ -76,7 +76,7 @@ func buildContextFromProto(proto *langpb.BuildContext) (BuildContext, error) { Schema: sch, Dependencies: proto.Dependencies, Config: moduleconfig.ModuleConfig{ - Dir: proto.ModuleConfig.Path, + Dir: proto.ModuleConfig.Dir, Language: "test", Realm: "test", Module: proto.ModuleConfig.Name, @@ -89,6 +89,14 @@ func buildContextFromProto(proto *langpb.BuildContext) (BuildContext, error) { }, nil } +func (p *mockExternalPluginClient) generateStubs(context.Context, *connect.Request[langpb.GenerateStubsRequest]) (*connect.Response[langpb.GenerateStubsResponse], error) { + panic("not implemented") +} + +func (p *mockExternalPluginClient) syncStubReferences(context.Context, *connect.Request[langpb.SyncStubReferencesRequest]) (*connect.Response[langpb.SyncStubReferencesResponse], error) { + panic("not implemented") +} + func (p *mockExternalPluginClient) build(ctx context.Context, req *connect.Request[langpb.BuildRequest]) (chan result.Result[*langpb.BuildEvent], streamCancelFunc, error) { p.buildEventsLock.Lock() defer p.buildEventsLock.Unlock() @@ -415,7 +423,7 @@ func (p *mockExternalPluginClient) publishBuildEvent(event *langpb.BuildEvent) { func beginBuild(ctx context.Context, plugin *externalPlugin, bctx BuildContext, autoRebuild bool) chan result.Result[BuildResult] { resultChan := make(chan result.Result[BuildResult]) go func() { - resultChan <- result.From(plugin.Build(ctx, "", bctx, []string{}, autoRebuild)) + resultChan <- result.From(plugin.Build(ctx, "", "", bctx, []string{}, autoRebuild)) }() // sleep to make sure impl has received the build context time.Sleep(300 * time.Millisecond) diff --git a/internal/buildengine/languageplugin/go_plugin.go b/internal/buildengine/languageplugin/go_plugin.go index 59c7288494..6127e1d882 100644 --- a/internal/buildengine/languageplugin/go_plugin.go +++ b/internal/buildengine/languageplugin/go_plugin.go @@ -3,18 +3,13 @@ package languageplugin import ( "context" "fmt" - "go/parser" - "go/token" - "io/fs" "path/filepath" "runtime" - "sort" - "strconv" "strings" "github.com/TBD54566975/scaffolder" "github.com/alecthomas/kong" - "golang.org/x/exp/maps" + "github.com/alecthomas/types/optional" goruntime "github.com/TBD54566975/ftl/go-runtime" "github.com/TBD54566975/ftl/go-runtime/compile" @@ -23,6 +18,7 @@ import ( "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/moduleconfig" "github.com/TBD54566975/ftl/internal/projectconfig" + "github.com/TBD54566975/ftl/internal/schema" "github.com/TBD54566975/ftl/internal/watch" ) @@ -116,51 +112,33 @@ func (p *goPlugin) CreateModule(ctx context.Context, projConfig projectconfig.Co func (p *goPlugin) GetDependencies(ctx context.Context, config moduleconfig.ModuleConfig) ([]string, error) { return p.internalPlugin.getDependencies(ctx, func() ([]string, error) { - dependencies := map[string]bool{} - fset := token.NewFileSet() - err := watch.WalkDir(config.Abs().Dir, func(path string, d fs.DirEntry) error { - if !d.IsDir() { - return nil - } - if strings.HasPrefix(d.Name(), "_") || d.Name() == "testdata" { - return watch.ErrSkip - } - pkgs, err := parser.ParseDir(fset, path, nil, parser.ImportsOnly) - if pkgs == nil { - return fmt.Errorf("could parse directory in search of dependencies: %w", err) - } - for _, pkg := range pkgs { - for _, file := range pkg.Files { - for _, imp := range file.Imports { - path, err := strconv.Unquote(imp.Path.Value) - if err != nil { - continue - } - if !strings.HasPrefix(path, "ftl/") { - continue - } - module := strings.Split(strings.TrimPrefix(path, "ftl/"), "/")[0] - if module == config.Module { - continue - } - dependencies[module] = true - } - } - } - return nil - }) - if err != nil { - return nil, fmt.Errorf("%s: failed to extract dependencies from Go module: %w", config.Module, err) - } - modules := maps.Keys(dependencies) - sort.Strings(modules) - return modules, nil + return compile.ExtractDependencies(config.Abs()) }) } -func buildGo(ctx context.Context, projectRoot string, bctx BuildContext, buildEnv []string, devMode bool, transaction watch.ModifyFilesTransaction) (BuildResult, error) { +func (p *goPlugin) GenerateStubs(ctx context.Context, dir string, module *schema.Module, moduleConfig moduleconfig.ModuleConfig, nativeModuleConfig optional.Option[moduleconfig.ModuleConfig]) error { + var absNativeModuleConfig optional.Option[moduleconfig.AbsModuleConfig] + if c, ok := nativeModuleConfig.Get(); ok { + absNativeModuleConfig = optional.Some(c.Abs()) + } + err := compile.GenerateStubs(ctx, dir, module, moduleConfig.Abs(), absNativeModuleConfig) + if err != nil { + return fmt.Errorf("could not generate stubs: %w", err) + } + return nil + +} +func (p *goPlugin) SyncStubReferences(ctx context.Context, config moduleconfig.ModuleConfig, dir string, moduleNames []string) error { + err := compile.SyncGeneratedStubReferences(ctx, config, dir, moduleNames) + if err != nil { + return fmt.Errorf("could not sync stub references: %w", err) + } + return nil +} + +func buildGo(ctx context.Context, projectRoot, stubsRoot string, bctx BuildContext, buildEnv []string, devMode bool, transaction watch.ModifyFilesTransaction) (BuildResult, error) { config := bctx.Config.Abs() - moduleSch, buildErrs, err := compile.Build(ctx, projectRoot, config.Dir, config, bctx.Schema, transaction, buildEnv, devMode) + moduleSch, buildErrs, err := compile.Build(ctx, projectRoot, stubsRoot, config, bctx.Schema, transaction, buildEnv, devMode) if err != nil { return BuildResult{}, CompilerBuildError{err: fmt.Errorf("failed to build module %q: %w", config.Module, err)} } diff --git a/internal/buildengine/languageplugin/java_plugin.go b/internal/buildengine/languageplugin/java_plugin.go index 1d163c86dc..65f120da7c 100644 --- a/internal/buildengine/languageplugin/java_plugin.go +++ b/internal/buildengine/languageplugin/java_plugin.go @@ -248,7 +248,7 @@ func extractKotlinFTLImports(self, dir string) ([]string, error) { return modules, nil } -func buildJava(ctx context.Context, projectRoot string, bctx BuildContext, buildEnv []string, devMode bool, transaction watch.ModifyFilesTransaction) (BuildResult, error) { +func buildJava(ctx context.Context, projectRoot, stubsRoot string, bctx BuildContext, buildEnv []string, devMode bool, transaction watch.ModifyFilesTransaction) (BuildResult, error) { config := bctx.Config.Abs() logger := log.FromContext(ctx) javaConfig, err := loadJavaConfig(config.LanguageConfig, config.Language) diff --git a/internal/buildengine/languageplugin/plugin.go b/internal/buildengine/languageplugin/plugin.go index cfb6944aa1..7efabf60df 100644 --- a/internal/buildengine/languageplugin/plugin.go +++ b/internal/buildengine/languageplugin/plugin.go @@ -9,6 +9,7 @@ import ( "github.com/alecthomas/kong" "github.com/alecthomas/types/either" + "github.com/alecthomas/types/optional" "github.com/alecthomas/types/pubsub" "github.com/alecthomas/types/result" @@ -97,7 +98,19 @@ type LanguagePlugin interface { // Build builds the module with the latest config and schema. // In dev mode, plugin is responsible for automatically rebuilding as relevant files within the module change, // and publishing these automatic builds updates to Updates(). - Build(ctx context.Context, projectRoot string, bctx BuildContext, buildEnv []string, rebuildAutomatically bool) (BuildResult, error) + Build(ctx context.Context, projectRoot, stubsRoot string, bctx BuildContext, buildEnv []string, rebuildAutomatically bool) (BuildResult, error) + + // Generate stubs for the given module. + GenerateStubs(ctx context.Context, dir string, module *schema.Module, moduleConfig moduleconfig.ModuleConfig, nativeModuleConfig optional.Option[moduleconfig.ModuleConfig]) error + + // SyncStubReferences is called when module stubs have been updated. This allows the plugin to update + // references to external modules, regardless of whether they are dependencies. + // + // For example, go plugin adds references to all modules into the go.work file so that tools can automatically + // import the modules when users start reference them. + // + // It is optional to do anything with this call. + SyncStubReferences(ctx context.Context, config moduleconfig.ModuleConfig, dir string, moduleNames []string) error // Kill stops the plugin and cleans up any resources. Kill() error @@ -125,6 +138,7 @@ type pluginCommand interface { type buildCommand struct { BuildContext projectRoot string + stubsRoot string buildEnv []string rebuildAutomatically bool @@ -142,7 +156,7 @@ type getDependenciesCommand struct { func (getDependenciesCommand) pluginCmd() {} -type buildFunc = func(ctx context.Context, projectRoot string, bctx BuildContext, buildEnv []string, rebuildAutomatically bool, transaction watch.ModifyFilesTransaction) (BuildResult, error) +type buildFunc = func(ctx context.Context, projectRoot, stubsRoot string, bctx BuildContext, buildEnv []string, rebuildAutomatically bool, transaction watch.ModifyFilesTransaction) (BuildResult, error) type CompilerBuildError struct { err error @@ -192,10 +206,19 @@ func (p *internalPlugin) Kill() error { return nil } -func (p *internalPlugin) Build(ctx context.Context, projectRoot string, bctx BuildContext, buildEnv []string, rebuildAutomatically bool) (BuildResult, error) { +func (p *internalPlugin) GenerateStubs(ctx context.Context, dir string, module *schema.Module, moduleConfig moduleconfig.ModuleConfig, nativeModuleConfig optional.Option[moduleconfig.ModuleConfig]) error { + return nil +} + +func (p *internalPlugin) SyncStubReferences(ctx context.Context, config moduleconfig.ModuleConfig, dir string, moduleNames []string) error { + return nil +} + +func (p *internalPlugin) Build(ctx context.Context, projectRoot, stubsRoot string, bctx BuildContext, buildEnv []string, rebuildAutomatically bool) (BuildResult, error) { cmd := buildCommand{ BuildContext: bctx, projectRoot: projectRoot, + stubsRoot: stubsRoot, buildEnv: buildEnv, rebuildAutomatically: rebuildAutomatically, result: make(chan either.Either[BuildResult, error]), @@ -245,6 +268,7 @@ func (p *internalPlugin) run(ctx context.Context) { // This is updated when given explicit build commands and used for automatic rebuilds var bctx BuildContext var projectRoot string + var stubsRoot string var buildEnv []string watching := false @@ -256,6 +280,7 @@ func (p *internalPlugin) run(ctx context.Context) { // update state bctx = c.BuildContext projectRoot = c.projectRoot + stubsRoot = c.stubsRoot buildEnv = c.buildEnv if watcher == nil { @@ -274,7 +299,7 @@ func (p *internalPlugin) run(ctx context.Context) { } // build - result, err := buildAndLoadResult(ctx, projectRoot, bctx, buildEnv, c.rebuildAutomatically, watcher, p.buildFunc) + result, err := buildAndLoadResult(ctx, projectRoot, stubsRoot, bctx, buildEnv, c.rebuildAutomatically, watcher, p.buildFunc) if err != nil { c.result <- either.RightOf[BuildResult](err) continue @@ -297,7 +322,7 @@ func (p *internalPlugin) run(ctx context.Context) { p.updates.Publish(AutoRebuildStartedEvent{Module: bctx.Config.Module}) p.updates.Publish(AutoRebuildEndedEvent{ Module: bctx.Config.Module, - Result: result.From(buildAndLoadResult(ctx, projectRoot, bctx, buildEnv, true, watcher, p.buildFunc)), + Result: result.From(buildAndLoadResult(ctx, projectRoot, stubsRoot, bctx, buildEnv, true, watcher, p.buildFunc)), }) case watch.WatchEventModuleAdded: // ignore @@ -312,7 +337,7 @@ func (p *internalPlugin) run(ctx context.Context) { } } -func buildAndLoadResult(ctx context.Context, projectRoot string, bctx BuildContext, buildEnv []string, devMode bool, watcher *watch.Watcher, build buildFunc) (BuildResult, error) { +func buildAndLoadResult(ctx context.Context, projectRoot, stubsRoot string, bctx BuildContext, buildEnv []string, devMode bool, watcher *watch.Watcher, build buildFunc) (BuildResult, error) { config := bctx.Config.Abs() release, err := flock.Acquire(ctx, filepath.Join(config.Dir, ".ftl.lock"), BuildLockTimeout) if err != nil { @@ -330,7 +355,7 @@ func buildAndLoadResult(ctx context.Context, projectRoot string, bctx BuildConte } transaction := watcher.GetTransaction(config.Dir) - result, err := build(ctx, projectRoot, bctx, buildEnv, devMode, transaction) + result, err := build(ctx, projectRoot, stubsRoot, bctx, buildEnv, devMode, transaction) if err != nil { return BuildResult{}, err } diff --git a/internal/buildengine/languageplugin/rust_plugin.go b/internal/buildengine/languageplugin/rust_plugin.go index 1063850ec7..f365386e0f 100644 --- a/internal/buildengine/languageplugin/rust_plugin.go +++ b/internal/buildengine/languageplugin/rust_plugin.go @@ -49,7 +49,7 @@ func (p *rustPlugin) GetDependencies(ctx context.Context, config moduleconfig.Mo return nil, fmt.Errorf("not implemented") } -func buildRust(ctx context.Context, projectRoot string, bctx BuildContext, buildEnv []string, devMode bool, transaction watch.ModifyFilesTransaction) (BuildResult, error) { +func buildRust(ctx context.Context, projectRoot, stubsRoot string, bctx BuildContext, buildEnv []string, devMode bool, transaction watch.ModifyFilesTransaction) (BuildResult, error) { config := bctx.Config.Abs() logger := log.FromContext(ctx) logger.Debugf("Using build command '%s'", config.Build) diff --git a/internal/buildengine/module.go b/internal/buildengine/module.go index 0f807e0940..fd84ad7977 100644 --- a/internal/buildengine/module.go +++ b/internal/buildengine/module.go @@ -7,16 +7,19 @@ import ( // Module represents an FTL module in the build engine type Module struct { - Config moduleconfig.ModuleConfig - Dependencies []string + Config moduleconfig.ModuleConfig // paths to deploy, relative to ModuleConfig.DeployDir Deploy []string + + // do not read directly, use Dependecies() instead + dependencies []string } -func (m Module) CopyWithDependencies(dependencies []string) Module { - module := reflect.DeepCopy(m) - module.Dependencies = dependencies - return module +func newModule(config moduleconfig.ModuleConfig) Module { + return Module{ + Config: config, + dependencies: []string{}, + } } func (m Module) CopyWithDeploy(files []string) Module { @@ -24,3 +27,42 @@ func (m Module) CopyWithDeploy(files []string) Module { module.Deploy = files return module } + +func (m Module) CopyWithDependencies(dependencies []string) Module { + module := reflect.DeepCopy(m) + module.dependencies = dependencies + return module +} + +// DependencyMode is an enum for dependency modes +type DependencyMode string + +const ( + Raw DependencyMode = "Raw" + AlwaysIncludeBuiltin DependencyMode = "AlwaysIncludingBuiltin" +) + +// Dependencies returns the dependencies of the module +// Mode allows us to control how dependencies are returned. +// +// When calling language plugins, use Raw mode to ensure plugins receive the same +// dependencies that were declared. +func (m Module) Dependencies(mode DependencyMode) []string { + dependencies := m.dependencies + switch mode { + case Raw: + // leave as is + case AlwaysIncludeBuiltin: + containsBuiltin := false + for _, dep := range dependencies { + if dep == "builtin" { + containsBuiltin = true + break + } + } + if !containsBuiltin { + dependencies = append(dependencies, "builtin") + } + } + return dependencies +} diff --git a/internal/buildengine/stubs.go b/internal/buildengine/stubs.go index d85185e0be..5e2be8d000 100644 --- a/internal/buildengine/stubs.go +++ b/internal/buildengine/stubs.go @@ -6,46 +6,112 @@ import ( "os" "path/filepath" - "github.com/TBD54566975/ftl/go-runtime/compile" + "github.com/alecthomas/types/optional" + "golang.org/x/sync/errgroup" + + "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/moduleconfig" "github.com/TBD54566975/ftl/internal/schema" ) +var buildDirName = ".ftl" + // GenerateStubs generates stubs for the given modules. // // Currently, only Go stubs are supported. Kotlin and other language stubs can be added in the future. -func GenerateStubs(ctx context.Context, projectRoot string, modules []*schema.Module, moduleConfigs []moduleconfig.ModuleConfig) error { - err := generateGoStubs(ctx, projectRoot, modules, moduleConfigs) +func GenerateStubs(ctx context.Context, projectRoot string, modules []*schema.Module, metas map[string]moduleMeta) error { + err := generateStubsForEachLanguage(ctx, projectRoot, modules, metas) if err != nil { return err } - return writeGenericSchemaFiles(ctx, projectRoot, modules, moduleConfigs) + return writeGenericSchemaFiles(modules, metas) } // CleanStubs removes all generated stubs. func CleanStubs(ctx context.Context, projectRoot string) error { - return cleanGoStubs(ctx, projectRoot) + logger := log.FromContext(ctx) + logger.Debugf("Deleting all generated stubs") + sharedFtlDir := filepath.Join(projectRoot, buildDirName) + + // Wipe the modules directory to ensure we don't have any stale modules. + err := os.RemoveAll(sharedFtlDir) + if err != nil { + return fmt.Errorf("failed to remove %s: %w", sharedFtlDir, err) + } + + return nil } // SyncStubReferences syncs the references in the generated stubs. // // For Go, this means updating all the go.work files to include all known modules in the shared stubbed modules directory. -func SyncStubReferences(ctx context.Context, projectRoot string, moduleNames []string, moduleConfigs []moduleconfig.ModuleConfig) error { - return syncGoStubReferences(ctx, projectRoot, moduleNames, moduleConfigs) +func SyncStubReferences(ctx context.Context, projectRoot string, moduleNames []string, metas map[string]moduleMeta) error { + wg, wgctx := errgroup.WithContext(ctx) + for _, meta := range metas { + stubsRoot := stubsLanguageDir(projectRoot, meta.module.Config.Language) + if err := meta.plugin.SyncStubReferences(wgctx, meta.module.Config, stubsRoot, moduleNames); err != nil { + return err //nolint:wrapcheck + } + return nil + } + err := wg.Wait() + if err != nil { + return fmt.Errorf("failed to sync go stub references: %w", err) + } + return nil } -func generateGoStubs(ctx context.Context, projectRoot string, modules []*schema.Module, moduleConfigs []moduleconfig.ModuleConfig) error { - sch := &schema.Schema{Modules: modules} - err := compile.GenerateStubsForModules(ctx, projectRoot, moduleConfigs, sch) +func stubsLanguageDir(projectRoot, language string) string { + return filepath.Join(projectRoot, buildDirName, language, "modules") +} + +func stubsModuleDir(projectRoot, language, module string) string { + return filepath.Join(stubsLanguageDir(projectRoot, language), module) +} + +func generateStubsForEachLanguage(ctx context.Context, projectRoot string, modules []*schema.Module, metas map[string]moduleMeta) error { + modulesByName := map[string]*schema.Module{} + for _, module := range modules { + modulesByName[module.Name] = module + } + metasByLanguage := map[string][]moduleMeta{} + for _, meta := range metas { + metasByLanguage[meta.module.Config.Language] = append(metasByLanguage[meta.module.Config.Language], meta) + } + wg, wgctx := errgroup.WithContext(ctx) + for language, metasForLang := range metasByLanguage { + for idx, module := range modules { + // spread the load across plugins + assignedMeta := metasForLang[idx%len(metasForLang)] + config := metas[module.Name].module.Config + wg.Go(func() error { + path := stubsModuleDir(projectRoot, language, module.Name) + err := os.MkdirAll(path, 0750) + if err != nil { + return fmt.Errorf("failed to create directory %s: %w", path, err) + } + var nativeConfig optional.Option[moduleconfig.ModuleConfig] + if config.Module == "builtin" || config.Language != language { + nativeConfig = optional.Some(assignedMeta.module.Config) + } + if err := assignedMeta.plugin.GenerateStubs(wgctx, path, module, config, nativeConfig); err != nil { + return err //nolint:wrapcheck + } + return nil + }) + } + } + err := wg.Wait() if err != nil { return fmt.Errorf("failed to generate go stubs: %w", err) } return nil } -func writeGenericSchemaFiles(ctx context.Context, projectRoot string, modules []*schema.Module, moduleConfigs []moduleconfig.ModuleConfig) error { +func writeGenericSchemaFiles(modules []*schema.Module, metas map[string]moduleMeta) error { sch := &schema.Schema{Modules: modules} - for _, module := range moduleConfigs { + for _, meta := range metas { + module := meta.module.Config if module.GeneratedSchemaDir == "" { continue } @@ -70,24 +136,5 @@ func writeGenericSchemaFiles(ctx context.Context, projectRoot string, modules [] } } } - err := compile.GenerateStubsForModules(ctx, projectRoot, moduleConfigs, sch) - if err != nil { - return fmt.Errorf("failed to generate go stubs: %w", err) - } - return nil -} -func cleanGoStubs(ctx context.Context, projectRoot string) error { - err := compile.CleanStubs(ctx, projectRoot) - if err != nil { - return fmt.Errorf("failed to clean go stubs: %w", err) - } - return nil -} - -func syncGoStubReferences(ctx context.Context, projectRoot string, moduleNames []string, moduleConfigs []moduleconfig.ModuleConfig) error { - err := compile.SyncGeneratedStubReferences(ctx, projectRoot, moduleNames, moduleConfigs) - if err != nil { - fmt.Printf("failed to sync go stub references: %v\n", err) - } return nil } diff --git a/internal/watch/watch.go b/internal/watch/watch.go index d3b9882c5d..f6692b4ac9 100644 --- a/internal/watch/watch.go +++ b/internal/watch/watch.go @@ -8,7 +8,6 @@ import ( "github.com/alecthomas/types/pubsub" - "github.com/TBD54566975/ftl/go-runtime/compile" "github.com/TBD54566975/ftl/internal/log" "github.com/TBD54566975/ftl/internal/maps" "github.com/TBD54566975/ftl/internal/moduleconfig" @@ -173,8 +172,6 @@ type modifyFilesTransaction struct { var _ ModifyFilesTransaction = (*modifyFilesTransaction)(nil) -var _ compile.ModifyFilesTransaction = (*modifyFilesTransaction)(nil) - func (t *modifyFilesTransaction) Begin() error { if t.isActive { return fmt.Errorf("transaction is already active")