├── .gitignore ├── README.md ├── backend ├── client.go ├── jsonServer.go ├── proto │ └── categories.pb.go └── server.go ├── frontend ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── proto │ ├── categories_grpc_web_pb.js │ └── categories_pb.js ├── public │ ├── favicon.png │ ├── global.css │ └── index.html ├── rollup.config.js ├── scripts │ └── setupTypeScript.js └── src │ ├── App.svelte │ ├── main.js │ └── services │ └── category.js └── proto └── categories.proto /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | **node_modules 3 | **public/build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [gRPC-web](https://github.com/grpc/grpc-web) *without* `envoy`! 2 | This project allows for the use of Google's `gRPC-web` `npm` package without implicitly requiring that one also use `envoy` alongside it. 3 | 4 | [![author - agrism](https://img.shields.io/badge/author_-agrism-2ea44f?logo=github&logoColor=%23ffffff&style=plastic)](https://github.com/agrism) 5 | 6 | ## PREREQUISITES 7 | _Standalone CLI tools for the most part._ 8 | ![Golang](https://img.shields.io/badge/GoLang-00ADD8?style=for-the-badge&logo=go&logoColor=white) 9 | ![npm - nodejs](https://img.shields.io/badge/Node.js-43853D?style=for-the-badge&logo=node.js&logoColor=white) 10 | 11 | > As for your package manager, obviously this all depends on OS. 12 | > **for Mac developers**, just use `Homebrew` for everything. 13 | > **for Linux devs**, you can easily use `apt` & `npm` together to get all necessary CLI tools and packages. 14 | > **for Windows devs** 15 | > ...just admit `batch` and `PowerShell` are doo-doo and install WSL2 16 | 17 | # `GETTING STARTED` 18 | This is the barebones installation process which I went through to configure my application environment correctly. 19 | 20 | ## **1.** Install Dependencies 21 | **MacOS** via `Homebrew` 22 | - `$ brew install protobuf` 23 | - `$ brew install protoc-gen-grpc-web` 24 | 25 | **Windows** WITH WSL / MSYS2 26 | and 27 | **LINUX** *(Tested: Debian 11.3)* 28 | > _the `-g` tag is used liberally here in order to do our best to guarantee that the package executable will end up referenced within the user $PATH variable._ 29 | 30 | - `$ npm install protoc-gen-grpc-web -g` (does this have to be Global or not?) 31 | - `$ npm install google-protobuf -g` 32 | - `$ npm install grpc-web -g` 33 | 34 | ## **2.** Install Required `Go` Packages 35 | - `$ go get -u google.golang.org/grpc` 36 | - `$ go get -u github.com/golang/protobuf/protoc-gen-go` 37 | 38 | ## **3.** Generate `protobuf` 39 | - `$ protoc -I proto proto/*.proto --proto_path=./proto --go_out=plugins=grpc:./backend/proto` 40 | - `$ protoc -I proto proto/*.proto --js_out=import_style=commonjs:./frontend/proto --grpc-web_out=import_style=commonjs,mode=grpcwebtext:./frontend/proto` 41 | 42 | --- 43 | 44 |
45 | 46 | # `DEPLOYMENT` 47 | here we discuss how to deploy the webserver [frontend](#frontend) via `npm`, as well as the `Golang` based [backend](#backend). 48 | 49 | ## FRONTEND 50 | _required `npm` packages & minimum versions_ 51 | (alongside `Sveltekit` obviously) 52 | - "google-protobuf": "^3.11.3", 53 | - "grpc-web": "^1.0.7" 54 | --- 55 | 56 | ### Example Deployment 57 | _example of FRONTEND deployment, with an output folder named as specified and utilized in the previous sections._ 58 | ```sh 59 | $ cd frontend 60 | $ npm install 61 | $ npm run dev 62 | ``` 63 | 64 | ## BACKEND 65 | _Backend setup is very easy, but somewhat limited in functionality at the moment._ 66 | 67 | ```sh 68 | $ cd backend 69 | $ go run server.go 70 | ``` 71 | 72 | --- 73 | [![author - agrism](https://img.shields.io/badge/author_-agrism-2ea44f?logo=github&logoColor=%23ffffff&style=plastic)](https://github.com/agrism) 74 | [![README - zudsniper](https://img.shields.io/badge/README.md_-zudsniper-abdab8?logo=github&logoColor=%23ffffff&style=plastic)](https://github.com/zudsniper) 75 | -------------------------------------------------------------------------------- /backend/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/agrism/grpc-web-svelte/backend/proto" 5 | "golang.org/x/net/context" 6 | "google.golang.org/grpc" 7 | "log" 8 | ) 9 | 10 | func main() { 11 | var conn *grpc.ClientConn 12 | conn, err := grpc.Dial(":9002", grpc.WithInsecure()) 13 | if err != nil { 14 | log.Fatalf("could not connect: %s", err) 15 | } 16 | defer conn.Close() 17 | 18 | categories := proto.NewCategoryServiceClient(conn) 19 | 20 | request := proto.IndexRequest{} 21 | 22 | response, err := categories.Index(context.Background(), &request) 23 | 24 | if err != nil{ 25 | log.Fatalf("Error when calling Index: %s", err) 26 | } 27 | 28 | log.Printf("Response from server: %s", response) 29 | } 30 | -------------------------------------------------------------------------------- /backend/jsonServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/Pallinder/go-randomdata" 7 | "log" 8 | "net/http" 9 | ) 10 | 11 | type Category struct { 12 | Id string `json:"id"` 13 | Name string `json:"name"` 14 | } 15 | 16 | func main() { 17 | 18 | http.HandleFunc("/", homePage) 19 | log.Fatal(http.ListenAndServe(":9003", nil)) 20 | } 21 | 22 | func enableCors(w *http.ResponseWriter) { 23 | (*w).Header().Set("Access-Control-Allow-Origin", "*") 24 | } 25 | 26 | func homePage(w http.ResponseWriter, r *http.Request){ 27 | 28 | enableCors(&w) 29 | 30 | w.Header().Add("Content-Type", "application/json") 31 | 32 | var cats []Category 33 | 34 | for i:= 0; i < 1000000; i++ { 35 | cats = append(cats, Category{ 36 | Id: fmt.Sprintf("%v", i), 37 | Name: randomdata.City(), 38 | }) 39 | } 40 | 41 | json.NewEncoder(w).Encode(cats) 42 | } -------------------------------------------------------------------------------- /backend/proto/categories.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.25.0-devel 4 | // protoc v3.13.0 5 | // source: categories.proto 6 | 7 | package proto 8 | 9 | import ( 10 | context "context" 11 | proto "github.com/golang/protobuf/proto" 12 | grpc "google.golang.org/grpc" 13 | codes "google.golang.org/grpc/codes" 14 | status "google.golang.org/grpc/status" 15 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 16 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 17 | reflect "reflect" 18 | sync "sync" 19 | ) 20 | 21 | const ( 22 | // Verify that this generated code is sufficiently up-to-date. 23 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 24 | // Verify that runtime/protoimpl is sufficiently up-to-date. 25 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 26 | ) 27 | 28 | // This is a compile-time assertion that a sufficiently up-to-date version 29 | // of the legacy proto package is being used. 30 | const _ = proto.ProtoPackageIsVersion4 31 | 32 | type Categories struct { 33 | state protoimpl.MessageState 34 | sizeCache protoimpl.SizeCache 35 | unknownFields protoimpl.UnknownFields 36 | 37 | Categories []*Category `protobuf:"bytes,1,rep,name=categories,proto3" json:"categories,omitempty"` 38 | } 39 | 40 | func (x *Categories) Reset() { 41 | *x = Categories{} 42 | if protoimpl.UnsafeEnabled { 43 | mi := &file_categories_proto_msgTypes[0] 44 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 45 | ms.StoreMessageInfo(mi) 46 | } 47 | } 48 | 49 | func (x *Categories) String() string { 50 | return protoimpl.X.MessageStringOf(x) 51 | } 52 | 53 | func (*Categories) ProtoMessage() {} 54 | 55 | func (x *Categories) ProtoReflect() protoreflect.Message { 56 | mi := &file_categories_proto_msgTypes[0] 57 | if protoimpl.UnsafeEnabled && x != nil { 58 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 59 | if ms.LoadMessageInfo() == nil { 60 | ms.StoreMessageInfo(mi) 61 | } 62 | return ms 63 | } 64 | return mi.MessageOf(x) 65 | } 66 | 67 | // Deprecated: Use Categories.ProtoReflect.Descriptor instead. 68 | func (*Categories) Descriptor() ([]byte, []int) { 69 | return file_categories_proto_rawDescGZIP(), []int{0} 70 | } 71 | 72 | func (x *Categories) GetCategories() []*Category { 73 | if x != nil { 74 | return x.Categories 75 | } 76 | return nil 77 | } 78 | 79 | type Category struct { 80 | state protoimpl.MessageState 81 | sizeCache protoimpl.SizeCache 82 | unknownFields protoimpl.UnknownFields 83 | 84 | Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` 85 | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` 86 | } 87 | 88 | func (x *Category) Reset() { 89 | *x = Category{} 90 | if protoimpl.UnsafeEnabled { 91 | mi := &file_categories_proto_msgTypes[1] 92 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 93 | ms.StoreMessageInfo(mi) 94 | } 95 | } 96 | 97 | func (x *Category) String() string { 98 | return protoimpl.X.MessageStringOf(x) 99 | } 100 | 101 | func (*Category) ProtoMessage() {} 102 | 103 | func (x *Category) ProtoReflect() protoreflect.Message { 104 | mi := &file_categories_proto_msgTypes[1] 105 | if protoimpl.UnsafeEnabled && x != nil { 106 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 107 | if ms.LoadMessageInfo() == nil { 108 | ms.StoreMessageInfo(mi) 109 | } 110 | return ms 111 | } 112 | return mi.MessageOf(x) 113 | } 114 | 115 | // Deprecated: Use Category.ProtoReflect.Descriptor instead. 116 | func (*Category) Descriptor() ([]byte, []int) { 117 | return file_categories_proto_rawDescGZIP(), []int{1} 118 | } 119 | 120 | func (x *Category) GetId() string { 121 | if x != nil { 122 | return x.Id 123 | } 124 | return "" 125 | } 126 | 127 | func (x *Category) GetName() string { 128 | if x != nil { 129 | return x.Name 130 | } 131 | return "" 132 | } 133 | 134 | type IndexRequest struct { 135 | state protoimpl.MessageState 136 | sizeCache protoimpl.SizeCache 137 | unknownFields protoimpl.UnknownFields 138 | } 139 | 140 | func (x *IndexRequest) Reset() { 141 | *x = IndexRequest{} 142 | if protoimpl.UnsafeEnabled { 143 | mi := &file_categories_proto_msgTypes[2] 144 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 145 | ms.StoreMessageInfo(mi) 146 | } 147 | } 148 | 149 | func (x *IndexRequest) String() string { 150 | return protoimpl.X.MessageStringOf(x) 151 | } 152 | 153 | func (*IndexRequest) ProtoMessage() {} 154 | 155 | func (x *IndexRequest) ProtoReflect() protoreflect.Message { 156 | mi := &file_categories_proto_msgTypes[2] 157 | if protoimpl.UnsafeEnabled && x != nil { 158 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 159 | if ms.LoadMessageInfo() == nil { 160 | ms.StoreMessageInfo(mi) 161 | } 162 | return ms 163 | } 164 | return mi.MessageOf(x) 165 | } 166 | 167 | // Deprecated: Use IndexRequest.ProtoReflect.Descriptor instead. 168 | func (*IndexRequest) Descriptor() ([]byte, []int) { 169 | return file_categories_proto_rawDescGZIP(), []int{2} 170 | } 171 | 172 | var File_categories_proto protoreflect.FileDescriptor 173 | 174 | var file_categories_proto_rawDesc = []byte{ 175 | 0x0a, 0x10, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 176 | 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3d, 0x0a, 0x0a, 0x43, 0x61, 0x74, 177 | 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 178 | 0x6f, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 179 | 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x61, 180 | 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x22, 0x2e, 0x0a, 0x08, 0x43, 0x61, 0x74, 0x65, 181 | 0x67, 0x6f, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 182 | 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 183 | 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x0e, 0x0a, 0x0c, 0x49, 0x6e, 0x64, 0x65, 184 | 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, 0x44, 0x0a, 0x0f, 0x43, 0x61, 0x74, 0x65, 185 | 0x67, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x05, 0x49, 186 | 0x6e, 0x64, 0x65, 0x78, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x6e, 0x64, 187 | 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 188 | 0x6f, 0x2e, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x22, 0x00, 0x42, 0x09, 189 | 0x5a, 0x07, 0x2e, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 190 | 0x33, 191 | } 192 | 193 | var ( 194 | file_categories_proto_rawDescOnce sync.Once 195 | file_categories_proto_rawDescData = file_categories_proto_rawDesc 196 | ) 197 | 198 | func file_categories_proto_rawDescGZIP() []byte { 199 | file_categories_proto_rawDescOnce.Do(func() { 200 | file_categories_proto_rawDescData = protoimpl.X.CompressGZIP(file_categories_proto_rawDescData) 201 | }) 202 | return file_categories_proto_rawDescData 203 | } 204 | 205 | var file_categories_proto_msgTypes = make([]protoimpl.MessageInfo, 3) 206 | var file_categories_proto_goTypes = []interface{}{ 207 | (*Categories)(nil), // 0: proto.Categories 208 | (*Category)(nil), // 1: proto.Category 209 | (*IndexRequest)(nil), // 2: proto.IndexRequest 210 | } 211 | var file_categories_proto_depIdxs = []int32{ 212 | 1, // 0: proto.Categories.categories:type_name -> proto.Category 213 | 2, // 1: proto.CategoryService.Index:input_type -> proto.IndexRequest 214 | 0, // 2: proto.CategoryService.Index:output_type -> proto.Categories 215 | 2, // [2:3] is the sub-list for method output_type 216 | 1, // [1:2] is the sub-list for method input_type 217 | 1, // [1:1] is the sub-list for extension type_name 218 | 1, // [1:1] is the sub-list for extension extendee 219 | 0, // [0:1] is the sub-list for field type_name 220 | } 221 | 222 | func init() { file_categories_proto_init() } 223 | func file_categories_proto_init() { 224 | if File_categories_proto != nil { 225 | return 226 | } 227 | if !protoimpl.UnsafeEnabled { 228 | file_categories_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 229 | switch v := v.(*Categories); i { 230 | case 0: 231 | return &v.state 232 | case 1: 233 | return &v.sizeCache 234 | case 2: 235 | return &v.unknownFields 236 | default: 237 | return nil 238 | } 239 | } 240 | file_categories_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 241 | switch v := v.(*Category); i { 242 | case 0: 243 | return &v.state 244 | case 1: 245 | return &v.sizeCache 246 | case 2: 247 | return &v.unknownFields 248 | default: 249 | return nil 250 | } 251 | } 252 | file_categories_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 253 | switch v := v.(*IndexRequest); i { 254 | case 0: 255 | return &v.state 256 | case 1: 257 | return &v.sizeCache 258 | case 2: 259 | return &v.unknownFields 260 | default: 261 | return nil 262 | } 263 | } 264 | } 265 | type x struct{} 266 | out := protoimpl.TypeBuilder{ 267 | File: protoimpl.DescBuilder{ 268 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 269 | RawDescriptor: file_categories_proto_rawDesc, 270 | NumEnums: 0, 271 | NumMessages: 3, 272 | NumExtensions: 0, 273 | NumServices: 1, 274 | }, 275 | GoTypes: file_categories_proto_goTypes, 276 | DependencyIndexes: file_categories_proto_depIdxs, 277 | MessageInfos: file_categories_proto_msgTypes, 278 | }.Build() 279 | File_categories_proto = out.File 280 | file_categories_proto_rawDesc = nil 281 | file_categories_proto_goTypes = nil 282 | file_categories_proto_depIdxs = nil 283 | } 284 | 285 | // Reference imports to suppress errors if they are not otherwise used. 286 | var _ context.Context 287 | var _ grpc.ClientConnInterface 288 | 289 | // This is a compile-time assertion to ensure that this generated file 290 | // is compatible with the grpc package it is being compiled against. 291 | const _ = grpc.SupportPackageIsVersion6 292 | 293 | // CategoryServiceClient is the client API for CategoryService service. 294 | // 295 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 296 | type CategoryServiceClient interface { 297 | Index(ctx context.Context, in *IndexRequest, opts ...grpc.CallOption) (*Categories, error) 298 | } 299 | 300 | type categoryServiceClient struct { 301 | cc grpc.ClientConnInterface 302 | } 303 | 304 | func NewCategoryServiceClient(cc grpc.ClientConnInterface) CategoryServiceClient { 305 | return &categoryServiceClient{cc} 306 | } 307 | 308 | func (c *categoryServiceClient) Index(ctx context.Context, in *IndexRequest, opts ...grpc.CallOption) (*Categories, error) { 309 | out := new(Categories) 310 | err := c.cc.Invoke(ctx, "/proto.CategoryService/Index", in, out, opts...) 311 | if err != nil { 312 | return nil, err 313 | } 314 | return out, nil 315 | } 316 | 317 | // CategoryServiceServer is the server API for CategoryService service. 318 | type CategoryServiceServer interface { 319 | Index(context.Context, *IndexRequest) (*Categories, error) 320 | } 321 | 322 | // UnimplementedCategoryServiceServer can be embedded to have forward compatible implementations. 323 | type UnimplementedCategoryServiceServer struct { 324 | } 325 | 326 | func (*UnimplementedCategoryServiceServer) Index(context.Context, *IndexRequest) (*Categories, error) { 327 | return nil, status.Errorf(codes.Unimplemented, "method Index not implemented") 328 | } 329 | 330 | func RegisterCategoryServiceServer(s *grpc.Server, srv CategoryServiceServer) { 331 | s.RegisterService(&_CategoryService_serviceDesc, srv) 332 | } 333 | 334 | func _CategoryService_Index_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 335 | in := new(IndexRequest) 336 | if err := dec(in); err != nil { 337 | return nil, err 338 | } 339 | if interceptor == nil { 340 | return srv.(CategoryServiceServer).Index(ctx, in) 341 | } 342 | info := &grpc.UnaryServerInfo{ 343 | Server: srv, 344 | FullMethod: "/proto.CategoryService/Index", 345 | } 346 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 347 | return srv.(CategoryServiceServer).Index(ctx, req.(*IndexRequest)) 348 | } 349 | return interceptor(ctx, in, info, handler) 350 | } 351 | 352 | var _CategoryService_serviceDesc = grpc.ServiceDesc{ 353 | ServiceName: "proto.CategoryService", 354 | HandlerType: (*CategoryServiceServer)(nil), 355 | Methods: []grpc.MethodDesc{ 356 | { 357 | MethodName: "Index", 358 | Handler: _CategoryService_Index_Handler, 359 | }, 360 | }, 361 | Streams: []grpc.StreamDesc{}, 362 | Metadata: "categories.proto", 363 | } 364 | -------------------------------------------------------------------------------- /backend/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/Pallinder/go-randomdata" 7 | "github.com/agrism/grpc-web-svelte/backend/proto" 8 | "github.com/improbable-eng/grpc-web/go/grpcweb" 9 | "google.golang.org/grpc" 10 | "log" 11 | "net/http" 12 | ) 13 | 14 | type RpcServer struct { 15 | Grpc *grpc.Server 16 | WrappedGrpc *grpcweb.WrappedGrpcServer 17 | } 18 | 19 | type CategoryServer struct{} 20 | 21 | func (instance * CategoryServer) Index(ctx context.Context, in *proto.IndexRequest) (*proto.Categories, error) { 22 | 23 | cat := proto.Categories{ 24 | Categories: []*proto.Category{}, 25 | } 26 | 27 | for i:= 0; i < 1000000; i++ { 28 | cat.Categories = append(cat.Categories, &proto.Category{ 29 | Id: fmt.Sprintf("%v", i), 30 | Name: randomdata.City(), 31 | }) 32 | } 33 | 34 | return &cat, nil 35 | } 36 | 37 | func NewServer() *RpcServer { 38 | var opts []grpc.ServerOption 39 | //opts = append(opts, ServerInterceptor()) 40 | opts = append(opts) 41 | 42 | // It's increase to 5MB the maximum size allowed for requests and responses 43 | opts = append(opts, grpc.MaxSendMsgSize(5*1024*1024*1024*1024)) 44 | opts = append(opts, grpc.MaxRecvMsgSize(5*1024*1024*1024*1024)) 45 | gs := grpc.NewServer(opts...) 46 | return &RpcServer{ 47 | Grpc: gs, 48 | WrappedGrpc: grpcweb.WrapServer(gs), 49 | } 50 | } 51 | 52 | func main() { 53 | rpcServer := NewServer() 54 | 55 | categoryServer := CategoryServer{} 56 | 57 | 58 | proto.RegisterCategoryServiceServer(rpcServer.Grpc, &categoryServer) 59 | 60 | Start(":9002", rpcServer) 61 | } 62 | 63 | func Start(httpPort string, rpcServer *RpcServer) { 64 | grpc := rpcServer.WrappedGrpc 65 | 66 | http.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) { 67 | allowCors(resp, req) 68 | if grpc.IsGrpcWebRequest(req) || grpc.IsAcceptableGrpcCorsRequest(req) { 69 | grpc.ServeHTTP(resp, req) 70 | } 71 | }) 72 | 73 | fmt.Println("HTTP server listening on", httpPort) 74 | err := http.ListenAndServe(httpPort, nil) 75 | if err != nil { 76 | log.Fatal("Failed to start a HTTP server:", err) 77 | } 78 | } 79 | 80 | func allowCors(resp http.ResponseWriter, req *http.Request) { 81 | resp.Header().Set("Access-Control-Allow-Origin", "*") 82 | resp.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") 83 | resp.Header().Set("Access-Control-Expose-Headers", "grpc-status, grpc-message") 84 | resp.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, XMLHttpRequest, x-user-agent, x-grpc-web, grpc-status, grpc-message") 85 | } -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | *Looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)* 2 | 3 | --- 4 | 5 | # svelte app 6 | 7 | This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template. 8 | 9 | To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit): 10 | 11 | ```bash 12 | npx degit sveltejs/template svelte-app 13 | cd svelte-app 14 | ``` 15 | 16 | *Note that you will need to have [Node.js](https://nodejs.org) installed.* 17 | 18 | 19 | ## Get started 20 | 21 | Install the dependencies... 22 | 23 | ```bash 24 | cd svelte-app 25 | npm install 26 | ``` 27 | 28 | ...then start [Rollup](https://rollupjs.org): 29 | 30 | ```bash 31 | npm run dev 32 | ``` 33 | 34 | Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes. 35 | 36 | By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`. 37 | 38 | If you're using [Visual Studio Code](https://code.visualstudio.com/) we recommend installing the official extension [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If you are using other editors you may need to install a plugin in order to get syntax highlighting and intellisense. 39 | 40 | ## Building and running in production mode 41 | 42 | To create an optimised version of the app: 43 | 44 | ```bash 45 | npm run build 46 | ``` 47 | 48 | You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com). 49 | 50 | 51 | ## Single-page app mode 52 | 53 | By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere. 54 | 55 | If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json: 56 | 57 | ```js 58 | "start": "sirv public --single" 59 | ``` 60 | 61 | ## Using TypeScript 62 | 63 | This template comes with a script to set up a TypeScript development environment, you can run it immediately after cloning the template with: 64 | 65 | ```bash 66 | node scripts/setupTypeScript.js 67 | ``` 68 | 69 | Or remove the script via: 70 | 71 | ```bash 72 | rm scripts/setupTypeScript.js 73 | ``` 74 | 75 | ## Deploying to the web 76 | 77 | ### With [Vercel](https://vercel.com) 78 | 79 | Install `vercel` if you haven't already: 80 | 81 | ```bash 82 | npm install -g vercel 83 | ``` 84 | 85 | Then, from within your project folder: 86 | 87 | ```bash 88 | cd public 89 | vercel deploy --name my-project 90 | ``` 91 | 92 | ### With [surge](https://surge.sh/) 93 | 94 | Install `surge` if you haven't already: 95 | 96 | ```bash 97 | npm install -g surge 98 | ``` 99 | 100 | Then, from within your project folder: 101 | 102 | ```bash 103 | npm run build 104 | surge public my-project.surge.sh 105 | ``` 106 | -------------------------------------------------------------------------------- /frontend/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-app", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.4", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.10.4", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 19 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.4", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 25 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.4", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | } 32 | }, 33 | "@polka/url": { 34 | "version": "1.0.0-next.11", 35 | "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.11.tgz", 36 | "integrity": "sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA==" 37 | }, 38 | "@rollup/plugin-commonjs": { 39 | "version": "14.0.0", 40 | "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-14.0.0.tgz", 41 | "integrity": "sha512-+PSmD9ePwTAeU106i9FRdc+Zb3XUWyW26mo5Atr2mk82hor8+nPwkztEjFo8/B1fJKfaQDg9aM2bzQkjhi7zOw==", 42 | "dev": true, 43 | "requires": { 44 | "@rollup/pluginutils": "^3.0.8", 45 | "commondir": "^1.0.1", 46 | "estree-walker": "^1.0.1", 47 | "glob": "^7.1.2", 48 | "is-reference": "^1.1.2", 49 | "magic-string": "^0.25.2", 50 | "resolve": "^1.11.0" 51 | } 52 | }, 53 | "@rollup/plugin-node-resolve": { 54 | "version": "8.4.0", 55 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz", 56 | "integrity": "sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ==", 57 | "dev": true, 58 | "requires": { 59 | "@rollup/pluginutils": "^3.1.0", 60 | "@types/resolve": "1.17.1", 61 | "builtin-modules": "^3.1.0", 62 | "deep-freeze": "^0.0.1", 63 | "deepmerge": "^4.2.2", 64 | "is-module": "^1.0.0", 65 | "resolve": "^1.17.0" 66 | } 67 | }, 68 | "@rollup/pluginutils": { 69 | "version": "3.1.0", 70 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", 71 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", 72 | "dev": true, 73 | "requires": { 74 | "@types/estree": "0.0.39", 75 | "estree-walker": "^1.0.1", 76 | "picomatch": "^2.2.2" 77 | } 78 | }, 79 | "@types/estree": { 80 | "version": "0.0.39", 81 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", 82 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", 83 | "dev": true 84 | }, 85 | "@types/node": { 86 | "version": "14.11.2", 87 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.2.tgz", 88 | "integrity": "sha512-jiE3QIxJ8JLNcb1Ps6rDbysDhN4xa8DJJvuC9prr6w+1tIh+QAbYyNF3tyiZNLDBIuBCf4KEcV2UvQm/V60xfA==", 89 | "dev": true 90 | }, 91 | "@types/resolve": { 92 | "version": "1.17.1", 93 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", 94 | "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", 95 | "dev": true, 96 | "requires": { 97 | "@types/node": "*" 98 | } 99 | }, 100 | "ansi-styles": { 101 | "version": "3.2.1", 102 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 103 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 104 | "dev": true, 105 | "requires": { 106 | "color-convert": "^1.9.0" 107 | } 108 | }, 109 | "anymatch": { 110 | "version": "3.1.1", 111 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 112 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 113 | "dev": true, 114 | "requires": { 115 | "normalize-path": "^3.0.0", 116 | "picomatch": "^2.0.4" 117 | } 118 | }, 119 | "async-limiter": { 120 | "version": "1.0.1", 121 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", 122 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", 123 | "dev": true 124 | }, 125 | "balanced-match": { 126 | "version": "1.0.0", 127 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 128 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 129 | "dev": true 130 | }, 131 | "binary-extensions": { 132 | "version": "2.1.0", 133 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", 134 | "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", 135 | "dev": true 136 | }, 137 | "brace-expansion": { 138 | "version": "1.1.11", 139 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 140 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 141 | "dev": true, 142 | "requires": { 143 | "balanced-match": "^1.0.0", 144 | "concat-map": "0.0.1" 145 | } 146 | }, 147 | "braces": { 148 | "version": "3.0.2", 149 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 150 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 151 | "dev": true, 152 | "requires": { 153 | "fill-range": "^7.0.1" 154 | } 155 | }, 156 | "buffer-from": { 157 | "version": "1.1.1", 158 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 159 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 160 | "dev": true 161 | }, 162 | "builtin-modules": { 163 | "version": "3.1.0", 164 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", 165 | "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", 166 | "dev": true 167 | }, 168 | "chalk": { 169 | "version": "2.4.2", 170 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 171 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 172 | "dev": true, 173 | "requires": { 174 | "ansi-styles": "^3.2.1", 175 | "escape-string-regexp": "^1.0.5", 176 | "supports-color": "^5.3.0" 177 | } 178 | }, 179 | "chokidar": { 180 | "version": "3.4.2", 181 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", 182 | "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", 183 | "dev": true, 184 | "requires": { 185 | "anymatch": "~3.1.1", 186 | "braces": "~3.0.2", 187 | "fsevents": "~2.1.2", 188 | "glob-parent": "~5.1.0", 189 | "is-binary-path": "~2.1.0", 190 | "is-glob": "~4.0.1", 191 | "normalize-path": "~3.0.0", 192 | "readdirp": "~3.4.0" 193 | } 194 | }, 195 | "color-convert": { 196 | "version": "1.9.3", 197 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 198 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 199 | "dev": true, 200 | "requires": { 201 | "color-name": "1.1.3" 202 | } 203 | }, 204 | "color-name": { 205 | "version": "1.1.3", 206 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 207 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 208 | "dev": true 209 | }, 210 | "commander": { 211 | "version": "2.20.3", 212 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 213 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 214 | "dev": true 215 | }, 216 | "commondir": { 217 | "version": "1.0.1", 218 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 219 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", 220 | "dev": true 221 | }, 222 | "concat-map": { 223 | "version": "0.0.1", 224 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 225 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 226 | "dev": true 227 | }, 228 | "console-clear": { 229 | "version": "1.1.1", 230 | "resolved": "https://registry.npmjs.org/console-clear/-/console-clear-1.1.1.tgz", 231 | "integrity": "sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ==" 232 | }, 233 | "deep-freeze": { 234 | "version": "0.0.1", 235 | "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz", 236 | "integrity": "sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=", 237 | "dev": true 238 | }, 239 | "deepmerge": { 240 | "version": "4.2.2", 241 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", 242 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", 243 | "dev": true 244 | }, 245 | "escape-string-regexp": { 246 | "version": "1.0.5", 247 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 248 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 249 | "dev": true 250 | }, 251 | "estree-walker": { 252 | "version": "1.0.1", 253 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", 254 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", 255 | "dev": true 256 | }, 257 | "fill-range": { 258 | "version": "7.0.1", 259 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 260 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 261 | "dev": true, 262 | "requires": { 263 | "to-regex-range": "^5.0.1" 264 | } 265 | }, 266 | "fs.realpath": { 267 | "version": "1.0.0", 268 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 269 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 270 | "dev": true 271 | }, 272 | "fsevents": { 273 | "version": "2.1.3", 274 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 275 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 276 | "dev": true, 277 | "optional": true 278 | }, 279 | "get-port": { 280 | "version": "3.2.0", 281 | "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", 282 | "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=" 283 | }, 284 | "glob": { 285 | "version": "7.1.6", 286 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 287 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 288 | "dev": true, 289 | "requires": { 290 | "fs.realpath": "^1.0.0", 291 | "inflight": "^1.0.4", 292 | "inherits": "2", 293 | "minimatch": "^3.0.4", 294 | "once": "^1.3.0", 295 | "path-is-absolute": "^1.0.0" 296 | } 297 | }, 298 | "glob-parent": { 299 | "version": "5.1.1", 300 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 301 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 302 | "dev": true, 303 | "requires": { 304 | "is-glob": "^4.0.1" 305 | } 306 | }, 307 | "google-protobuf": { 308 | "version": "3.13.0", 309 | "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.13.0.tgz", 310 | "integrity": "sha512-ZIf3qfLFayVrPvAjeKKxO5FRF1/NwRxt6Dko+fWEMuHwHbZx8/fcaAao9b0wCM6kr8qeg2te8XTpyuvKuD9aKw==", 311 | "dev": true 312 | }, 313 | "grpc-web": { 314 | "version": "1.2.1", 315 | "resolved": "https://registry.npmjs.org/grpc-web/-/grpc-web-1.2.1.tgz", 316 | "integrity": "sha512-ibBaJPzfMVuLPgaST9w0kZl60s+SnkPBQp6QKdpEr85tpc1gXW2QDqSne9xiyiym0logDfdUSm4aX5h9YBA2mw==", 317 | "dev": true 318 | }, 319 | "has-flag": { 320 | "version": "3.0.0", 321 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 322 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 323 | "dev": true 324 | }, 325 | "inflight": { 326 | "version": "1.0.6", 327 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 328 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 329 | "dev": true, 330 | "requires": { 331 | "once": "^1.3.0", 332 | "wrappy": "1" 333 | } 334 | }, 335 | "inherits": { 336 | "version": "2.0.4", 337 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 338 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 339 | "dev": true 340 | }, 341 | "is-binary-path": { 342 | "version": "2.1.0", 343 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 344 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 345 | "dev": true, 346 | "requires": { 347 | "binary-extensions": "^2.0.0" 348 | } 349 | }, 350 | "is-extglob": { 351 | "version": "2.1.1", 352 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 353 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 354 | "dev": true 355 | }, 356 | "is-glob": { 357 | "version": "4.0.1", 358 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 359 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 360 | "dev": true, 361 | "requires": { 362 | "is-extglob": "^2.1.1" 363 | } 364 | }, 365 | "is-module": { 366 | "version": "1.0.0", 367 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 368 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", 369 | "dev": true 370 | }, 371 | "is-number": { 372 | "version": "7.0.0", 373 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 374 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 375 | "dev": true 376 | }, 377 | "is-reference": { 378 | "version": "1.2.1", 379 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", 380 | "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", 381 | "dev": true, 382 | "requires": { 383 | "@types/estree": "*" 384 | } 385 | }, 386 | "jest-worker": { 387 | "version": "26.3.0", 388 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz", 389 | "integrity": "sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==", 390 | "dev": true, 391 | "requires": { 392 | "@types/node": "*", 393 | "merge-stream": "^2.0.0", 394 | "supports-color": "^7.0.0" 395 | }, 396 | "dependencies": { 397 | "has-flag": { 398 | "version": "4.0.0", 399 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 400 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 401 | "dev": true 402 | }, 403 | "supports-color": { 404 | "version": "7.2.0", 405 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 406 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 407 | "dev": true, 408 | "requires": { 409 | "has-flag": "^4.0.0" 410 | } 411 | } 412 | } 413 | }, 414 | "js-tokens": { 415 | "version": "4.0.0", 416 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 417 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 418 | "dev": true 419 | }, 420 | "kleur": { 421 | "version": "3.0.3", 422 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", 423 | "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" 424 | }, 425 | "livereload": { 426 | "version": "0.9.1", 427 | "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.1.tgz", 428 | "integrity": "sha512-9g7sua11kkyZNo2hLRCG3LuZZwqexoyEyecSlV8cAsfAVVCZqLzVir6XDqmH0r+Vzgnd5LrdHDMyjtFnJQLAYw==", 429 | "dev": true, 430 | "requires": { 431 | "chokidar": "^3.3.0", 432 | "livereload-js": "^3.1.0", 433 | "opts": ">= 1.2.0", 434 | "ws": "^6.2.1" 435 | } 436 | }, 437 | "livereload-js": { 438 | "version": "3.3.1", 439 | "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.3.1.tgz", 440 | "integrity": "sha512-CBu1gTEfzVhlOK1WASKAAJ9Qx1fHECTq0SUB67sfxwQssopTyvzqTlgl+c0h9pZ6V+Fzd2rc510ppuNusg9teQ==", 441 | "dev": true 442 | }, 443 | "local-access": { 444 | "version": "1.0.1", 445 | "resolved": "https://registry.npmjs.org/local-access/-/local-access-1.0.1.tgz", 446 | "integrity": "sha512-ykt2pgN0aqIy6KQC1CqdWTWkmUwNgaOS6dcpHVjyBJONA+Xi7AtSB1vuxC/U/0tjIP3wcRudwQk1YYzUvzk2bA==" 447 | }, 448 | "magic-string": { 449 | "version": "0.25.7", 450 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", 451 | "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", 452 | "dev": true, 453 | "requires": { 454 | "sourcemap-codec": "^1.4.4" 455 | } 456 | }, 457 | "merge-stream": { 458 | "version": "2.0.0", 459 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 460 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 461 | "dev": true 462 | }, 463 | "mime": { 464 | "version": "2.4.6", 465 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", 466 | "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" 467 | }, 468 | "minimatch": { 469 | "version": "3.0.4", 470 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 471 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 472 | "dev": true, 473 | "requires": { 474 | "brace-expansion": "^1.1.7" 475 | } 476 | }, 477 | "mri": { 478 | "version": "1.1.6", 479 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz", 480 | "integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==" 481 | }, 482 | "normalize-path": { 483 | "version": "3.0.0", 484 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 485 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 486 | "dev": true 487 | }, 488 | "once": { 489 | "version": "1.4.0", 490 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 491 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 492 | "dev": true, 493 | "requires": { 494 | "wrappy": "1" 495 | } 496 | }, 497 | "opts": { 498 | "version": "2.0.2", 499 | "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz", 500 | "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", 501 | "dev": true 502 | }, 503 | "path-is-absolute": { 504 | "version": "1.0.1", 505 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 506 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 507 | "dev": true 508 | }, 509 | "path-parse": { 510 | "version": "1.0.6", 511 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 512 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 513 | "dev": true 514 | }, 515 | "picomatch": { 516 | "version": "2.2.2", 517 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 518 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", 519 | "dev": true 520 | }, 521 | "randombytes": { 522 | "version": "2.1.0", 523 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 524 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 525 | "dev": true, 526 | "requires": { 527 | "safe-buffer": "^5.1.0" 528 | } 529 | }, 530 | "readdirp": { 531 | "version": "3.4.0", 532 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", 533 | "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", 534 | "dev": true, 535 | "requires": { 536 | "picomatch": "^2.2.1" 537 | } 538 | }, 539 | "require-relative": { 540 | "version": "0.8.7", 541 | "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", 542 | "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", 543 | "dev": true 544 | }, 545 | "resolve": { 546 | "version": "1.17.0", 547 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 548 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 549 | "dev": true, 550 | "requires": { 551 | "path-parse": "^1.0.6" 552 | } 553 | }, 554 | "rollup": { 555 | "version": "2.28.2", 556 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.28.2.tgz", 557 | "integrity": "sha512-8txbsFBFLmm9Xdt4ByTOGa9Muonmc8MfNjnGAR8U8scJlF1ZW7AgNZa7aqBXaKtlvnYP/ab++fQIq9dB9NWUbg==", 558 | "dev": true, 559 | "requires": { 560 | "fsevents": "~2.1.2" 561 | } 562 | }, 563 | "rollup-plugin-livereload": { 564 | "version": "2.0.0", 565 | "resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.0.tgz", 566 | "integrity": "sha512-oC/8NqumGYuphkqrfszOHUUIwzKsaHBICw6QRwT5uD07gvePTS+HW+GFwu6f9K8W02CUuTvtIM9AWJrbj4wE1A==", 567 | "dev": true, 568 | "requires": { 569 | "livereload": "^0.9.1" 570 | } 571 | }, 572 | "rollup-plugin-svelte": { 573 | "version": "6.0.1", 574 | "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-6.0.1.tgz", 575 | "integrity": "sha512-kS9/JZMBNgpKTqVKlwV8mhmGwxu8NiNf6+n5ZzdZ8yDp3+ADqjf8Au+JNEpoOn6kLlh1hLS2Gsa76k9RP57HDQ==", 576 | "dev": true, 577 | "requires": { 578 | "require-relative": "^0.8.7", 579 | "rollup-pluginutils": "^2.8.2", 580 | "sourcemap-codec": "^1.4.8" 581 | } 582 | }, 583 | "rollup-plugin-terser": { 584 | "version": "7.0.2", 585 | "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", 586 | "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", 587 | "dev": true, 588 | "requires": { 589 | "@babel/code-frame": "^7.10.4", 590 | "jest-worker": "^26.2.1", 591 | "serialize-javascript": "^4.0.0", 592 | "terser": "^5.0.0" 593 | } 594 | }, 595 | "rollup-pluginutils": { 596 | "version": "2.8.2", 597 | "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", 598 | "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", 599 | "dev": true, 600 | "requires": { 601 | "estree-walker": "^0.6.1" 602 | }, 603 | "dependencies": { 604 | "estree-walker": { 605 | "version": "0.6.1", 606 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", 607 | "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", 608 | "dev": true 609 | } 610 | } 611 | }, 612 | "sade": { 613 | "version": "1.7.4", 614 | "resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz", 615 | "integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==", 616 | "requires": { 617 | "mri": "^1.1.0" 618 | } 619 | }, 620 | "safe-buffer": { 621 | "version": "5.2.1", 622 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 623 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 624 | "dev": true 625 | }, 626 | "semiver": { 627 | "version": "1.1.0", 628 | "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", 629 | "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==" 630 | }, 631 | "serialize-javascript": { 632 | "version": "4.0.0", 633 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", 634 | "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", 635 | "dev": true, 636 | "requires": { 637 | "randombytes": "^2.1.0" 638 | } 639 | }, 640 | "sirv": { 641 | "version": "1.0.6", 642 | "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.6.tgz", 643 | "integrity": "sha512-LRGu7Op4Xl9hhigOy2kcB53zAYTjNDdpooey49dIU0cMdpOv9ithVf7nstk3jvs8EhMiT/VORoyazZYGgw4vnA==", 644 | "requires": { 645 | "@polka/url": "^1.0.0-next.9", 646 | "mime": "^2.3.1", 647 | "totalist": "^1.0.0" 648 | } 649 | }, 650 | "sirv-cli": { 651 | "version": "1.0.6", 652 | "resolved": "https://registry.npmjs.org/sirv-cli/-/sirv-cli-1.0.6.tgz", 653 | "integrity": "sha512-K/iY1OHG7hTw4GzLoqMhwzKCbgWmx5joYAAF2+CwyiamWCpVzAgNVWgAc0JmSA2Gf3wseov05il2QbFTGTZMVg==", 654 | "requires": { 655 | "console-clear": "^1.1.0", 656 | "get-port": "^3.2.0", 657 | "kleur": "^3.0.0", 658 | "local-access": "^1.0.1", 659 | "sade": "^1.6.0", 660 | "semiver": "^1.0.0", 661 | "sirv": "^1.0.6", 662 | "tinydate": "^1.0.0" 663 | } 664 | }, 665 | "source-map": { 666 | "version": "0.7.3", 667 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", 668 | "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", 669 | "dev": true 670 | }, 671 | "source-map-support": { 672 | "version": "0.5.19", 673 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 674 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 675 | "dev": true, 676 | "requires": { 677 | "buffer-from": "^1.0.0", 678 | "source-map": "^0.6.0" 679 | }, 680 | "dependencies": { 681 | "source-map": { 682 | "version": "0.6.1", 683 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 684 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 685 | "dev": true 686 | } 687 | } 688 | }, 689 | "sourcemap-codec": { 690 | "version": "1.4.8", 691 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", 692 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", 693 | "dev": true 694 | }, 695 | "supports-color": { 696 | "version": "5.5.0", 697 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 698 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 699 | "dev": true, 700 | "requires": { 701 | "has-flag": "^3.0.0" 702 | } 703 | }, 704 | "svelte": { 705 | "version": "3.29.0", 706 | "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.29.0.tgz", 707 | "integrity": "sha512-f+A65eyOQ5ujETLy+igNXtlr6AEjAQLYd1yJE1VwNiXMQO5Z/Vmiy3rL+zblV/9jd7rtTTWqO1IcuXsP2Qv0OA==", 708 | "dev": true 709 | }, 710 | "terser": { 711 | "version": "5.3.3", 712 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.3.tgz", 713 | "integrity": "sha512-vRQDIlD+2Pg8YMwVK9kMM3yGylG95EIwzBai1Bw7Ot4OBfn3VP1TZn3EWx4ep2jERN/AmnVaTiGuelZSN7ds/A==", 714 | "dev": true, 715 | "requires": { 716 | "commander": "^2.20.0", 717 | "source-map": "~0.7.2", 718 | "source-map-support": "~0.5.19" 719 | } 720 | }, 721 | "tinydate": { 722 | "version": "1.3.0", 723 | "resolved": "https://registry.npmjs.org/tinydate/-/tinydate-1.3.0.tgz", 724 | "integrity": "sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==" 725 | }, 726 | "to-regex-range": { 727 | "version": "5.0.1", 728 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 729 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 730 | "dev": true, 731 | "requires": { 732 | "is-number": "^7.0.0" 733 | } 734 | }, 735 | "totalist": { 736 | "version": "1.1.0", 737 | "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", 738 | "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==" 739 | }, 740 | "wrappy": { 741 | "version": "1.0.2", 742 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 743 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 744 | "dev": true 745 | }, 746 | "ws": { 747 | "version": "6.2.1", 748 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", 749 | "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", 750 | "dev": true, 751 | "requires": { 752 | "async-limiter": "~1.0.0" 753 | } 754 | } 755 | } 756 | } 757 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-app", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "build": "rollup -c", 6 | "dev": "rollup -c -w", 7 | "start": "sirv public" 8 | }, 9 | "devDependencies": { 10 | "@rollup/plugin-commonjs": "^14.0.0", 11 | "@rollup/plugin-node-resolve": "^8.0.0", 12 | "rollup": "^2.3.4", 13 | "rollup-plugin-livereload": "^2.0.0", 14 | "rollup-plugin-svelte": "^6.0.0", 15 | "rollup-plugin-terser": "^7.0.0", 16 | "svelte": "^3.0.0", 17 | "google-protobuf": "^3.11.3", 18 | "grpc-web": "^1.0.7" 19 | }, 20 | "dependencies": { 21 | "sirv-cli": "^1.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /frontend/proto/categories_grpc_web_pb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview gRPC-Web generated client stub for proto 3 | * @enhanceable 4 | * @public 5 | */ 6 | 7 | // GENERATED CODE -- DO NOT EDIT! 8 | 9 | 10 | /* eslint-disable */ 11 | // @ts-nocheck 12 | 13 | 14 | 15 | const grpc = {}; 16 | grpc.web = require('grpc-web'); 17 | 18 | const proto = {}; 19 | proto.proto = require('./categories_pb.js'); 20 | 21 | /** 22 | * @param {string} hostname 23 | * @param {?Object} credentials 24 | * @param {?Object} options 25 | * @constructor 26 | * @struct 27 | * @final 28 | */ 29 | proto.proto.CategoryServiceClient = 30 | function(hostname, credentials, options) { 31 | if (!options) options = {}; 32 | options['format'] = 'text'; 33 | 34 | /** 35 | * @private @const {!grpc.web.GrpcWebClientBase} The client 36 | */ 37 | this.client_ = new grpc.web.GrpcWebClientBase(options); 38 | 39 | /** 40 | * @private @const {string} The hostname 41 | */ 42 | this.hostname_ = hostname; 43 | 44 | }; 45 | 46 | 47 | /** 48 | * @param {string} hostname 49 | * @param {?Object} credentials 50 | * @param {?Object} options 51 | * @constructor 52 | * @struct 53 | * @final 54 | */ 55 | proto.proto.CategoryServicePromiseClient = 56 | function(hostname, credentials, options) { 57 | if (!options) options = {}; 58 | options['format'] = 'text'; 59 | 60 | /** 61 | * @private @const {!grpc.web.GrpcWebClientBase} The client 62 | */ 63 | this.client_ = new grpc.web.GrpcWebClientBase(options); 64 | 65 | /** 66 | * @private @const {string} The hostname 67 | */ 68 | this.hostname_ = hostname; 69 | 70 | }; 71 | 72 | 73 | /** 74 | * @const 75 | * @type {!grpc.web.MethodDescriptor< 76 | * !proto.proto.IndexRequest, 77 | * !proto.proto.Categories>} 78 | */ 79 | const methodDescriptor_CategoryService_Index = new grpc.web.MethodDescriptor( 80 | '/proto.CategoryService/Index', 81 | grpc.web.MethodType.UNARY, 82 | proto.proto.IndexRequest, 83 | proto.proto.Categories, 84 | /** 85 | * @param {!proto.proto.IndexRequest} request 86 | * @return {!Uint8Array} 87 | */ 88 | function(request) { 89 | return request.serializeBinary(); 90 | }, 91 | proto.proto.Categories.deserializeBinary 92 | ); 93 | 94 | 95 | /** 96 | * @const 97 | * @type {!grpc.web.AbstractClientBase.MethodInfo< 98 | * !proto.proto.IndexRequest, 99 | * !proto.proto.Categories>} 100 | */ 101 | const methodInfo_CategoryService_Index = new grpc.web.AbstractClientBase.MethodInfo( 102 | proto.proto.Categories, 103 | /** 104 | * @param {!proto.proto.IndexRequest} request 105 | * @return {!Uint8Array} 106 | */ 107 | function(request) { 108 | return request.serializeBinary(); 109 | }, 110 | proto.proto.Categories.deserializeBinary 111 | ); 112 | 113 | 114 | /** 115 | * @param {!proto.proto.IndexRequest} request The 116 | * request proto 117 | * @param {?Object} metadata User defined 118 | * call metadata 119 | * @param {function(?grpc.web.Error, ?proto.proto.Categories)} 120 | * callback The callback function(error, response) 121 | * @return {!grpc.web.ClientReadableStream|undefined} 122 | * The XHR Node Readable Stream 123 | */ 124 | proto.proto.CategoryServiceClient.prototype.index = 125 | function(request, metadata, callback) { 126 | return this.client_.rpcCall(this.hostname_ + 127 | '/proto.CategoryService/Index', 128 | request, 129 | metadata || {}, 130 | methodDescriptor_CategoryService_Index, 131 | callback); 132 | }; 133 | 134 | 135 | /** 136 | * @param {!proto.proto.IndexRequest} request The 137 | * request proto 138 | * @param {?Object} metadata User defined 139 | * call metadata 140 | * @return {!Promise} 141 | * Promise that resolves to the response 142 | */ 143 | proto.proto.CategoryServicePromiseClient.prototype.index = 144 | function(request, metadata) { 145 | return this.client_.unaryCall(this.hostname_ + 146 | '/proto.CategoryService/Index', 147 | request, 148 | metadata || {}, 149 | methodDescriptor_CategoryService_Index); 150 | }; 151 | 152 | 153 | module.exports = proto.proto; 154 | 155 | -------------------------------------------------------------------------------- /frontend/proto/categories_pb.js: -------------------------------------------------------------------------------- 1 | // source: categories.proto 2 | /** 3 | * @fileoverview 4 | * @enhanceable 5 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 6 | * field starts with 'MSG_' and isn't a translatable message. 7 | * @public 8 | */ 9 | // GENERATED CODE -- DO NOT EDIT! 10 | 11 | var jspb = require('google-protobuf'); 12 | var goog = jspb; 13 | var global = Function('return this')(); 14 | 15 | goog.exportSymbol('proto.proto.Categories', null, global); 16 | goog.exportSymbol('proto.proto.Category', null, global); 17 | goog.exportSymbol('proto.proto.IndexRequest', null, global); 18 | /** 19 | * Generated by JsPbCodeGenerator. 20 | * @param {Array=} opt_data Optional initial data array, typically from a 21 | * server response, or constructed directly in Javascript. The array is used 22 | * in place and becomes part of the constructed object. It is not cloned. 23 | * If no data is provided, the constructed object will be empty, but still 24 | * valid. 25 | * @extends {jspb.Message} 26 | * @constructor 27 | */ 28 | proto.proto.Categories = function(opt_data) { 29 | jspb.Message.initialize(this, opt_data, 0, -1, proto.proto.Categories.repeatedFields_, null); 30 | }; 31 | goog.inherits(proto.proto.Categories, jspb.Message); 32 | if (goog.DEBUG && !COMPILED) { 33 | /** 34 | * @public 35 | * @override 36 | */ 37 | proto.proto.Categories.displayName = 'proto.proto.Categories'; 38 | } 39 | /** 40 | * Generated by JsPbCodeGenerator. 41 | * @param {Array=} opt_data Optional initial data array, typically from a 42 | * server response, or constructed directly in Javascript. The array is used 43 | * in place and becomes part of the constructed object. It is not cloned. 44 | * If no data is provided, the constructed object will be empty, but still 45 | * valid. 46 | * @extends {jspb.Message} 47 | * @constructor 48 | */ 49 | proto.proto.Category = function(opt_data) { 50 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 51 | }; 52 | goog.inherits(proto.proto.Category, jspb.Message); 53 | if (goog.DEBUG && !COMPILED) { 54 | /** 55 | * @public 56 | * @override 57 | */ 58 | proto.proto.Category.displayName = 'proto.proto.Category'; 59 | } 60 | /** 61 | * Generated by JsPbCodeGenerator. 62 | * @param {Array=} opt_data Optional initial data array, typically from a 63 | * server response, or constructed directly in Javascript. The array is used 64 | * in place and becomes part of the constructed object. It is not cloned. 65 | * If no data is provided, the constructed object will be empty, but still 66 | * valid. 67 | * @extends {jspb.Message} 68 | * @constructor 69 | */ 70 | proto.proto.IndexRequest = function(opt_data) { 71 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 72 | }; 73 | goog.inherits(proto.proto.IndexRequest, jspb.Message); 74 | if (goog.DEBUG && !COMPILED) { 75 | /** 76 | * @public 77 | * @override 78 | */ 79 | proto.proto.IndexRequest.displayName = 'proto.proto.IndexRequest'; 80 | } 81 | 82 | /** 83 | * List of repeated fields within this message type. 84 | * @private {!Array} 85 | * @const 86 | */ 87 | proto.proto.Categories.repeatedFields_ = [1]; 88 | 89 | 90 | 91 | if (jspb.Message.GENERATE_TO_OBJECT) { 92 | /** 93 | * Creates an object representation of this proto. 94 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 95 | * Optional fields that are not set will be set to undefined. 96 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 97 | * For the list of reserved names please see: 98 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 99 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 100 | * JSPB instance for transitional soy proto support: 101 | * http://goto/soy-param-migration 102 | * @return {!Object} 103 | */ 104 | proto.proto.Categories.prototype.toObject = function(opt_includeInstance) { 105 | return proto.proto.Categories.toObject(opt_includeInstance, this); 106 | }; 107 | 108 | 109 | /** 110 | * Static version of the {@see toObject} method. 111 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 112 | * the JSPB instance for transitional soy proto support: 113 | * http://goto/soy-param-migration 114 | * @param {!proto.proto.Categories} msg The msg instance to transform. 115 | * @return {!Object} 116 | * @suppress {unusedLocalVariables} f is only used for nested messages 117 | */ 118 | proto.proto.Categories.toObject = function(includeInstance, msg) { 119 | var f, obj = { 120 | categoriesList: jspb.Message.toObjectList(msg.getCategoriesList(), 121 | proto.proto.Category.toObject, includeInstance) 122 | }; 123 | 124 | if (includeInstance) { 125 | obj.$jspbMessageInstance = msg; 126 | } 127 | return obj; 128 | }; 129 | } 130 | 131 | 132 | /** 133 | * Deserializes binary data (in protobuf wire format). 134 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 135 | * @return {!proto.proto.Categories} 136 | */ 137 | proto.proto.Categories.deserializeBinary = function(bytes) { 138 | var reader = new jspb.BinaryReader(bytes); 139 | var msg = new proto.proto.Categories; 140 | return proto.proto.Categories.deserializeBinaryFromReader(msg, reader); 141 | }; 142 | 143 | 144 | /** 145 | * Deserializes binary data (in protobuf wire format) from the 146 | * given reader into the given message object. 147 | * @param {!proto.proto.Categories} msg The message object to deserialize into. 148 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 149 | * @return {!proto.proto.Categories} 150 | */ 151 | proto.proto.Categories.deserializeBinaryFromReader = function(msg, reader) { 152 | while (reader.nextField()) { 153 | if (reader.isEndGroup()) { 154 | break; 155 | } 156 | var field = reader.getFieldNumber(); 157 | switch (field) { 158 | case 1: 159 | var value = new proto.proto.Category; 160 | reader.readMessage(value,proto.proto.Category.deserializeBinaryFromReader); 161 | msg.addCategories(value); 162 | break; 163 | default: 164 | reader.skipField(); 165 | break; 166 | } 167 | } 168 | return msg; 169 | }; 170 | 171 | 172 | /** 173 | * Serializes the message to binary data (in protobuf wire format). 174 | * @return {!Uint8Array} 175 | */ 176 | proto.proto.Categories.prototype.serializeBinary = function() { 177 | var writer = new jspb.BinaryWriter(); 178 | proto.proto.Categories.serializeBinaryToWriter(this, writer); 179 | return writer.getResultBuffer(); 180 | }; 181 | 182 | 183 | /** 184 | * Serializes the given message to binary data (in protobuf wire 185 | * format), writing to the given BinaryWriter. 186 | * @param {!proto.proto.Categories} message 187 | * @param {!jspb.BinaryWriter} writer 188 | * @suppress {unusedLocalVariables} f is only used for nested messages 189 | */ 190 | proto.proto.Categories.serializeBinaryToWriter = function(message, writer) { 191 | var f = undefined; 192 | f = message.getCategoriesList(); 193 | if (f.length > 0) { 194 | writer.writeRepeatedMessage( 195 | 1, 196 | f, 197 | proto.proto.Category.serializeBinaryToWriter 198 | ); 199 | } 200 | }; 201 | 202 | 203 | /** 204 | * repeated Category categories = 1; 205 | * @return {!Array} 206 | */ 207 | proto.proto.Categories.prototype.getCategoriesList = function() { 208 | return /** @type{!Array} */ ( 209 | jspb.Message.getRepeatedWrapperField(this, proto.proto.Category, 1)); 210 | }; 211 | 212 | 213 | /** 214 | * @param {!Array} value 215 | * @return {!proto.proto.Categories} returns this 216 | */ 217 | proto.proto.Categories.prototype.setCategoriesList = function(value) { 218 | return jspb.Message.setRepeatedWrapperField(this, 1, value); 219 | }; 220 | 221 | 222 | /** 223 | * @param {!proto.proto.Category=} opt_value 224 | * @param {number=} opt_index 225 | * @return {!proto.proto.Category} 226 | */ 227 | proto.proto.Categories.prototype.addCategories = function(opt_value, opt_index) { 228 | return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.proto.Category, opt_index); 229 | }; 230 | 231 | 232 | /** 233 | * Clears the list making it empty but non-null. 234 | * @return {!proto.proto.Categories} returns this 235 | */ 236 | proto.proto.Categories.prototype.clearCategoriesList = function() { 237 | return this.setCategoriesList([]); 238 | }; 239 | 240 | 241 | 242 | 243 | 244 | if (jspb.Message.GENERATE_TO_OBJECT) { 245 | /** 246 | * Creates an object representation of this proto. 247 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 248 | * Optional fields that are not set will be set to undefined. 249 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 250 | * For the list of reserved names please see: 251 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 252 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 253 | * JSPB instance for transitional soy proto support: 254 | * http://goto/soy-param-migration 255 | * @return {!Object} 256 | */ 257 | proto.proto.Category.prototype.toObject = function(opt_includeInstance) { 258 | return proto.proto.Category.toObject(opt_includeInstance, this); 259 | }; 260 | 261 | 262 | /** 263 | * Static version of the {@see toObject} method. 264 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 265 | * the JSPB instance for transitional soy proto support: 266 | * http://goto/soy-param-migration 267 | * @param {!proto.proto.Category} msg The msg instance to transform. 268 | * @return {!Object} 269 | * @suppress {unusedLocalVariables} f is only used for nested messages 270 | */ 271 | proto.proto.Category.toObject = function(includeInstance, msg) { 272 | var f, obj = { 273 | id: jspb.Message.getFieldWithDefault(msg, 1, ""), 274 | name: jspb.Message.getFieldWithDefault(msg, 2, "") 275 | }; 276 | 277 | if (includeInstance) { 278 | obj.$jspbMessageInstance = msg; 279 | } 280 | return obj; 281 | }; 282 | } 283 | 284 | 285 | /** 286 | * Deserializes binary data (in protobuf wire format). 287 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 288 | * @return {!proto.proto.Category} 289 | */ 290 | proto.proto.Category.deserializeBinary = function(bytes) { 291 | var reader = new jspb.BinaryReader(bytes); 292 | var msg = new proto.proto.Category; 293 | return proto.proto.Category.deserializeBinaryFromReader(msg, reader); 294 | }; 295 | 296 | 297 | /** 298 | * Deserializes binary data (in protobuf wire format) from the 299 | * given reader into the given message object. 300 | * @param {!proto.proto.Category} msg The message object to deserialize into. 301 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 302 | * @return {!proto.proto.Category} 303 | */ 304 | proto.proto.Category.deserializeBinaryFromReader = function(msg, reader) { 305 | while (reader.nextField()) { 306 | if (reader.isEndGroup()) { 307 | break; 308 | } 309 | var field = reader.getFieldNumber(); 310 | switch (field) { 311 | case 1: 312 | var value = /** @type {string} */ (reader.readString()); 313 | msg.setId(value); 314 | break; 315 | case 2: 316 | var value = /** @type {string} */ (reader.readString()); 317 | msg.setName(value); 318 | break; 319 | default: 320 | reader.skipField(); 321 | break; 322 | } 323 | } 324 | return msg; 325 | }; 326 | 327 | 328 | /** 329 | * Serializes the message to binary data (in protobuf wire format). 330 | * @return {!Uint8Array} 331 | */ 332 | proto.proto.Category.prototype.serializeBinary = function() { 333 | var writer = new jspb.BinaryWriter(); 334 | proto.proto.Category.serializeBinaryToWriter(this, writer); 335 | return writer.getResultBuffer(); 336 | }; 337 | 338 | 339 | /** 340 | * Serializes the given message to binary data (in protobuf wire 341 | * format), writing to the given BinaryWriter. 342 | * @param {!proto.proto.Category} message 343 | * @param {!jspb.BinaryWriter} writer 344 | * @suppress {unusedLocalVariables} f is only used for nested messages 345 | */ 346 | proto.proto.Category.serializeBinaryToWriter = function(message, writer) { 347 | var f = undefined; 348 | f = message.getId(); 349 | if (f.length > 0) { 350 | writer.writeString( 351 | 1, 352 | f 353 | ); 354 | } 355 | f = message.getName(); 356 | if (f.length > 0) { 357 | writer.writeString( 358 | 2, 359 | f 360 | ); 361 | } 362 | }; 363 | 364 | 365 | /** 366 | * optional string id = 1; 367 | * @return {string} 368 | */ 369 | proto.proto.Category.prototype.getId = function() { 370 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); 371 | }; 372 | 373 | 374 | /** 375 | * @param {string} value 376 | * @return {!proto.proto.Category} returns this 377 | */ 378 | proto.proto.Category.prototype.setId = function(value) { 379 | return jspb.Message.setProto3StringField(this, 1, value); 380 | }; 381 | 382 | 383 | /** 384 | * optional string name = 2; 385 | * @return {string} 386 | */ 387 | proto.proto.Category.prototype.getName = function() { 388 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); 389 | }; 390 | 391 | 392 | /** 393 | * @param {string} value 394 | * @return {!proto.proto.Category} returns this 395 | */ 396 | proto.proto.Category.prototype.setName = function(value) { 397 | return jspb.Message.setProto3StringField(this, 2, value); 398 | }; 399 | 400 | 401 | 402 | 403 | 404 | if (jspb.Message.GENERATE_TO_OBJECT) { 405 | /** 406 | * Creates an object representation of this proto. 407 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 408 | * Optional fields that are not set will be set to undefined. 409 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 410 | * For the list of reserved names please see: 411 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 412 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 413 | * JSPB instance for transitional soy proto support: 414 | * http://goto/soy-param-migration 415 | * @return {!Object} 416 | */ 417 | proto.proto.IndexRequest.prototype.toObject = function(opt_includeInstance) { 418 | return proto.proto.IndexRequest.toObject(opt_includeInstance, this); 419 | }; 420 | 421 | 422 | /** 423 | * Static version of the {@see toObject} method. 424 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 425 | * the JSPB instance for transitional soy proto support: 426 | * http://goto/soy-param-migration 427 | * @param {!proto.proto.IndexRequest} msg The msg instance to transform. 428 | * @return {!Object} 429 | * @suppress {unusedLocalVariables} f is only used for nested messages 430 | */ 431 | proto.proto.IndexRequest.toObject = function(includeInstance, msg) { 432 | var f, obj = { 433 | 434 | }; 435 | 436 | if (includeInstance) { 437 | obj.$jspbMessageInstance = msg; 438 | } 439 | return obj; 440 | }; 441 | } 442 | 443 | 444 | /** 445 | * Deserializes binary data (in protobuf wire format). 446 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 447 | * @return {!proto.proto.IndexRequest} 448 | */ 449 | proto.proto.IndexRequest.deserializeBinary = function(bytes) { 450 | var reader = new jspb.BinaryReader(bytes); 451 | var msg = new proto.proto.IndexRequest; 452 | return proto.proto.IndexRequest.deserializeBinaryFromReader(msg, reader); 453 | }; 454 | 455 | 456 | /** 457 | * Deserializes binary data (in protobuf wire format) from the 458 | * given reader into the given message object. 459 | * @param {!proto.proto.IndexRequest} msg The message object to deserialize into. 460 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 461 | * @return {!proto.proto.IndexRequest} 462 | */ 463 | proto.proto.IndexRequest.deserializeBinaryFromReader = function(msg, reader) { 464 | while (reader.nextField()) { 465 | if (reader.isEndGroup()) { 466 | break; 467 | } 468 | var field = reader.getFieldNumber(); 469 | switch (field) { 470 | default: 471 | reader.skipField(); 472 | break; 473 | } 474 | } 475 | return msg; 476 | }; 477 | 478 | 479 | /** 480 | * Serializes the message to binary data (in protobuf wire format). 481 | * @return {!Uint8Array} 482 | */ 483 | proto.proto.IndexRequest.prototype.serializeBinary = function() { 484 | var writer = new jspb.BinaryWriter(); 485 | proto.proto.IndexRequest.serializeBinaryToWriter(this, writer); 486 | return writer.getResultBuffer(); 487 | }; 488 | 489 | 490 | /** 491 | * Serializes the given message to binary data (in protobuf wire 492 | * format), writing to the given BinaryWriter. 493 | * @param {!proto.proto.IndexRequest} message 494 | * @param {!jspb.BinaryWriter} writer 495 | * @suppress {unusedLocalVariables} f is only used for nested messages 496 | */ 497 | proto.proto.IndexRequest.serializeBinaryToWriter = function(message, writer) { 498 | var f = undefined; 499 | }; 500 | 501 | 502 | goog.object.extend(exports, proto.proto); 503 | -------------------------------------------------------------------------------- /frontend/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agrism/grpc-web-svelte/4b630ef6de3f3801557caf21bad9e26871e6f937/frontend/public/favicon.png -------------------------------------------------------------------------------- /frontend/public/global.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | position: relative; 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | body { 8 | color: #333; 9 | margin: 0; 10 | padding: 8px; 11 | box-sizing: border-box; 12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 13 | } 14 | 15 | a { 16 | color: rgb(0,100,200); 17 | text-decoration: none; 18 | } 19 | 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | 24 | a:visited { 25 | color: rgb(0,80,160); 26 | } 27 | 28 | label { 29 | display: block; 30 | } 31 | 32 | input, button, select, textarea { 33 | font-family: inherit; 34 | font-size: inherit; 35 | -webkit-padding: 0.4em 0; 36 | padding: 0.4em; 37 | margin: 0 0 0.5em 0; 38 | box-sizing: border-box; 39 | border: 1px solid #ccc; 40 | border-radius: 2px; 41 | } 42 | 43 | input:disabled { 44 | color: #ccc; 45 | } 46 | 47 | button { 48 | color: #333; 49 | background-color: #f4f4f4; 50 | outline: none; 51 | } 52 | 53 | button:disabled { 54 | color: #999; 55 | } 56 | 57 | button:not(:disabled):active { 58 | background-color: #ddd; 59 | } 60 | 61 | button:focus { 62 | border-color: #666; 63 | } 64 | -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Svelte app 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /frontend/rollup.config.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte'; 2 | import resolve from '@rollup/plugin-node-resolve'; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import livereload from 'rollup-plugin-livereload'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | 7 | const production = !process.env.ROLLUP_WATCH; 8 | 9 | function serve() { 10 | let server; 11 | 12 | function toExit() { 13 | if (server) server.kill(0); 14 | } 15 | 16 | return { 17 | writeBundle() { 18 | if (server) return; 19 | server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { 20 | stdio: ['ignore', 'inherit', 'inherit'], 21 | shell: true 22 | }); 23 | 24 | process.on('SIGTERM', toExit); 25 | process.on('exit', toExit); 26 | } 27 | }; 28 | } 29 | 30 | export default { 31 | input: 'src/main.js', 32 | output: { 33 | sourcemap: true, 34 | format: 'iife', 35 | name: 'app', 36 | file: 'public/build/bundle.js' 37 | }, 38 | plugins: [ 39 | svelte({ 40 | // enable run-time checks when not in production 41 | dev: !production, 42 | // we'll extract any component CSS out into 43 | // a separate file - better for performance 44 | css: css => { 45 | css.write('bundle.css'); 46 | } 47 | }), 48 | 49 | // If you have external dependencies installed from 50 | // npm, you'll most likely need these plugins. In 51 | // some cases you'll need additional configuration - 52 | // consult the documentation for details: 53 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 54 | resolve({ 55 | browser: true, 56 | dedupe: ['svelte'] 57 | }), 58 | commonjs(), 59 | 60 | // In dev mode, call `npm run start` once 61 | // the bundle has been generated 62 | !production && serve(), 63 | 64 | // Watch the `public` directory and refresh the 65 | // browser on changes when not in production 66 | !production && livereload('public'), 67 | 68 | // If we're building for production (npm run build 69 | // instead of npm run dev), minify 70 | production && terser() 71 | ], 72 | watch: { 73 | clearScreen: false 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /frontend/scripts/setupTypeScript.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** This script modifies the project to support TS code in .svelte files like: 4 | 5 | 8 | 9 | As well as validating the code for CI. 10 | */ 11 | 12 | /** To work on this script: 13 | rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template 14 | */ 15 | 16 | const fs = require("fs") 17 | const path = require("path") 18 | const { argv } = require("process") 19 | 20 | const projectRoot = argv[2] || path.join(__dirname, "..") 21 | 22 | // Add deps to pkg.json 23 | const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8")) 24 | packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, { 25 | "svelte-check": "^1.0.0", 26 | "svelte-preprocess": "^4.0.0", 27 | "@rollup/plugin-typescript": "^6.0.0", 28 | "typescript": "^3.9.3", 29 | "tslib": "^2.0.0", 30 | "@tsconfig/svelte": "^1.0.0" 31 | }) 32 | 33 | // Add script for checking 34 | packageJSON.scripts = Object.assign(packageJSON.scripts, { 35 | "validate": "svelte-check" 36 | }) 37 | 38 | // Write the package JSON 39 | fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " ")) 40 | 41 | // mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too 42 | const beforeMainJSPath = path.join(projectRoot, "src", "main.js") 43 | const afterMainTSPath = path.join(projectRoot, "src", "main.ts") 44 | fs.renameSync(beforeMainJSPath, afterMainTSPath) 45 | 46 | // Switch the app.svelte file to use TS 47 | const appSveltePath = path.join(projectRoot, "src", "App.svelte") 48 | let appFile = fs.readFileSync(appSveltePath, "utf8") 49 | appFile = appFile.replace(" 61 | 62 |
63 |

gRPC-web!

64 |

Visit the Svelte tutorial to learn how to build Svelte apps.

65 | 66 | 67 |
    68 | {#each cats.slice(0,50) as cat} 69 |
  • 70 | {cat.id} - {cat.name} 71 |
  • 72 | {/each} 73 |
74 | 75 | 76 |
    77 | {#each catsJson.slice(0,50) as cat} 78 |
  • 79 | {cat.id} - {cat.name} 80 |
  • 81 | {/each} 82 |
83 | 84 | 85 |
86 | 87 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte'; 2 | 3 | const app = new App({ 4 | target: document.body, 5 | props: { 6 | name: 'world---' 7 | } 8 | }); 9 | 10 | export default app; -------------------------------------------------------------------------------- /frontend/src/services/category.js: -------------------------------------------------------------------------------- 1 | export default class{ 2 | constructor(deps){ 3 | this.tokenKey = deps.tokenKey 4 | this.proto = deps.proto 5 | this.client = new deps.proto.CategoryClient('http://localhost:9002', null, null) 6 | } 7 | 8 | index (){ 9 | const req = new this.proto.IndexRequest() 10 | return this.client.index(req, {}).then(res=>{ 11 | return res.getCategoriesList().map(category=>{ 12 | return { 13 | id: category.getId(), 14 | name: category.getName(), 15 | } 16 | }); 17 | }) 18 | } 19 | } -------------------------------------------------------------------------------- /proto/categories.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package proto; 4 | option go_package = ".;proto"; 5 | 6 | service CategoryService { 7 | rpc Index(IndexRequest) returns (Categories) {} 8 | } 9 | 10 | message Categories { 11 | repeated Category categories = 1; 12 | } 13 | 14 | message Category { 15 | string id = 1; 16 | string name = 2; 17 | } 18 | 19 | message IndexRequest {} 20 | 21 | --------------------------------------------------------------------------------