├── .changeset └── config.json ├── .eslintignore ├── .eslintrc.cjs ├── .github ├── pull_request_template.md └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── README.md ├── example ├── envoy │ ├── docker-compose.yaml │ └── envoy.yaml ├── frontend_react │ ├── package-lock.json │ ├── package.json │ ├── protos │ │ ├── StocksServiceClientPb.ts │ │ ├── stocks.proto │ │ ├── stocks_grpc_web_pb.d.ts │ │ ├── stocks_grpc_web_pb.js │ │ ├── stocks_pb.d.ts │ │ └── stocks_pb.js │ ├── src │ │ ├── App.tsx │ │ ├── components │ │ │ ├── ButtonGroupComponent.tsx │ │ │ ├── Layout.tsx │ │ │ ├── Navbar.tsx │ │ │ ├── Responses.tsx │ │ │ └── Store.tsx │ │ ├── index.html │ │ ├── index.tsx │ │ └── useGrpcExpress.ts │ └── webpack.config.js └── server │ ├── .eslintrc.js │ ├── package-lock.json │ ├── package.json │ ├── protos │ ├── google │ │ └── protobuf │ │ │ ├── descriptor 2.ts │ │ │ └── descriptor.ts │ ├── stocks 2.proto │ ├── stocks 2.ts │ ├── stocks.client 2.ts │ ├── stocks.client.ts │ ├── stocks.grpc-client 2.ts │ ├── stocks.grpc-client.ts │ ├── stocks.grpc-server 2.ts │ ├── stocks.grpc-server.ts │ ├── stocks.proto │ ├── stocks.ts │ ├── todos 2.proto │ └── todos.proto │ ├── src │ ├── client_dynamic 2.ts │ ├── client_dynamic.ts │ ├── client_static 2.ts │ ├── client_static.ts │ ├── database │ │ ├── database 2.ts │ │ └── database.ts │ ├── server_dynamic 2.ts │ ├── server_dynamic.ts │ ├── server_static 2.ts │ └── server_static.ts │ └── tsconfig.json ├── package-lock.json ├── package.json ├── src ├── CacheStore.test.ts ├── CacheStore.ts ├── DeserializerStore.test.ts ├── DeserializerStore.ts ├── Heap.test.ts ├── Heap.ts ├── PendingStore.test.ts ├── PendingStore.ts ├── grpcExpressClient.test.ts ├── grpcExpressClient.ts └── index.ts ├── tsconfig.json ├── use_grpc_express ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .npmignore ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── index.ts │ └── useGrpcExpress.ts └── tsconfig.json └── vite.config.js /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "public", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint:recommended', 4 | 'plugin:@typescript-eslint/recommended', 5 | 'prettier', 6 | ], 7 | parser: '@typescript-eslint/parser', 8 | plugins: ['@typescript-eslint'], 9 | root: true, 10 | env: { 11 | node: true, 12 | }, 13 | rules: { 14 | '@typescript-eslint/no-explicit-any': 'off', 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Request Type 2 | 3 | - [ ] New Feature 4 | - [ ] Bug Fix 5 | - [ ] Performance Improvement 6 | - [ ] Documentation Update 7 | - [ ] Code Refactoring 8 | - [ ] Other (Specify): ____________________ 9 | 10 | ## Description 11 | 12 | 13 | ## Issue 14 | 15 | 16 | ## Changes Made 17 | 18 | 19 | ## Screenshots or GIFs (if applicable) 20 | 21 | 22 | ## Testing 23 | 24 | 25 | ## Additional Notes 26 | 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | jobs: 11 | ci: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v3 17 | 18 | - name: Set up Node.js 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: '18' 22 | 23 | - name: Install dependencies 24 | run: npm install 25 | 26 | - name: Run CI 27 | run: npm run ci 28 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | jobs: 11 | publish: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v3 17 | 18 | - name: Set up Node.js 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: "18" 22 | 23 | - name: Install dependencies 24 | run: npm install 25 | 26 | - name: Create Release Pull Request or Publish 27 | id: changesets 28 | uses: changesets/action@v1 29 | with: 30 | publish: npm run npm-publish 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/**/node_modules 2 | **/**/dist 3 | **/**/.env 4 | **/**/protoc 5 | **/**/.DS_Store 6 | .vscode -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .changeset 2 | .github 3 | example 4 | src 5 | node_modules 6 | .eslintignore 7 | .eslintrc.cjs 8 | .gitignore 9 | tsconfig.json 10 | vite.config.ts 11 | README.md 12 | use_grpc_express -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @grpcexpress/grpcexpress 2 | 3 | ## 0.4.0 4 | 5 | ### Minor Changes 6 | 7 | - Updated the size limit logic for the cache store 8 | 9 | ## 0.3.1 10 | 11 | ### Patch Changes 12 | 13 | - Attempt to fix the github publish workflow error 14 | 15 | ## 0.3.0 16 | 17 | ### Minor Changes 18 | 19 | - Implemented invalidation method 20 | 21 | ### Patch Changes 22 | 23 | - Updated dependencies 24 | - @grpcexpress/grpcexpress@0.3.0 25 | 26 | ## 0.2.2 27 | 28 | ### Patch Changes 29 | 30 | - Implemented a cost aware algorithm 31 | 32 | ## 0.2.1 33 | 34 | ### Patch Changes 35 | 36 | - Refactored the cache expiration from setTimeout to checking an expiration date 37 | 38 | ## 0.2.0 39 | 40 | ### Minor Changes 41 | 42 | - Moved the source code to the top level. Removed the concepts folder which contains test code. 43 | 44 | ### Patch Changes 45 | 46 | - 9d4c6b3: Set up tsup and changeset. Set up github action to publish to NPM 47 | - 9d4c6b3: Moved changeset to the root directory 48 | - 9d4c6b3: Corrected the publish workflow 49 | - 9d4c6b3: Created an index.ts file to export the grpcExpressClient 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gRPC Express 2 | 3 | ![Version badge](https://img.shields.io/badge/version-1.0.0-blue.svg) 4 | 5 | `gRPCExpress` is a gRPC-web caching library built with TypeScript. It enhances the native functionalities of the `grpc-web` library by providing advanced caching capabilities. 6 | 7 | ## Features 8 | 9 | - **Enhanced Caching**: Extends the grpc-web's ServiceClient class to incorporate advanced caching functionalities. 10 | - **Efficiency**: A pub-sub pattern to sequence repetitive function calls, reducing network usage. 11 | - **Cost-aware Caching**: An efficient algorithm that maintains cache size constraints while ensuring relevant data remains cached. 12 | - **Unit Testing**: Tested using ViTest. 13 | 14 | ## Prerequisites 15 | 16 | Before you begin, ensure you have the `grpc-web` dependency installed in your project and the services stub generated with protoc. 17 | 18 | ```bash 19 | protoc -I=protos stocks.proto --js_out=import_style=commonjs,binary:protos --grpc-web_out=import_style=commonjs+dts,mode=grpcweb:protos 20 | ``` 21 | 22 | ## Installation 23 | 24 | Install the library using npm: 25 | 26 | ```bash 27 | npm i @grpcexpress/grpcexpress 28 | ``` 29 | 30 | ## Basic Usage 31 | 32 | ```typescript 33 | import { grpcExpressClient } from "@grpcexpress/grpcexpress"; 34 | const Client = grpcExpressClient(ServiceClient); 35 | const client = new Client(url); 36 | const response = await client.myMethod(message); 37 | ``` 38 | 39 | ## Advanced Usage 40 | 41 | ### Setting Default Cache Duration 42 | 43 | Set default cache duration (in ms) for all method calls, the default duration is 10 minutes: 44 | 45 | ```typescript 46 | const Client = grpcExpressClient(ServiceClient, 10000); 47 | ``` 48 | 49 | ### Skipping Caching 50 | 51 | To skip caching for a particular call: 52 | 53 | ```typescript 54 | const response = await client.myMethod(message, { 55 | cacheOptions: { cache: "nocache:" }, 56 | }); 57 | ``` 58 | 59 | ### Custom Cache Expiration 60 | 61 | Specify a custom cache expiration duration (in ms) for a particular response: 62 | 63 | ```typescript 64 | const response = await client.myMethod(message, { 65 | cacheOptions: { duration: 10000 }, 66 | }); 67 | ``` 68 | 69 | ## License 70 | 71 | This project is licensed under the MIT License. 72 | 73 | ## Credits 74 | 75 | Junwen Zhang 76 | Arthur Griffith 77 | Murat Agrali 78 | Shi Yu Liu 79 | -------------------------------------------------------------------------------- /example/envoy/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | envoy: 4 | image: envoyproxy/envoy:dev-adb237c8ddfbe56e21a43d729b3bc40aed9f860b 5 | volumes: 6 | - ./envoy.yaml:/etc/envoy/envoy.yaml:ro 7 | ports: 8 | - '8080:8080' 9 | - '3000:3000' 10 | command: ['envoy', '-c', '/etc/envoy/envoy.yaml'] 11 | -------------------------------------------------------------------------------- /example/envoy/envoy.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | access_log_path: /tmp/admin_access.log 3 | address: 4 | socket_address: { address: 0.0.0.0, port_value: 9901 } 5 | 6 | static_resources: 7 | listeners: 8 | - name: listener_0 9 | address: 10 | socket_address: { address: 0.0.0.0, port_value: 8080 } 11 | filter_chains: 12 | - filters: 13 | - name: envoy.filters.network.http_connection_manager 14 | typed_config: 15 | '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 16 | codec_type: auto 17 | stat_prefix: ingress_http 18 | route_config: 19 | name: local_route 20 | virtual_hosts: 21 | - name: local_service 22 | domains: ['*'] 23 | routes: 24 | - match: { prefix: '/' } 25 | route: 26 | cluster: stocks_service 27 | timeout: 0s 28 | max_stream_duration: 29 | grpc_timeout_header_max: 0s 30 | cors: 31 | allow_origin_string_match: 32 | - prefix: '*' 33 | allow_methods: GET, PUT, DELETE, POST, OPTIONS 34 | allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout 35 | max_age: '1728000' 36 | expose_headers: custom-header-1,grpc-status,grpc-message 37 | http_filters: 38 | - name: envoy.filters.http.grpc_web 39 | typed_config: 40 | '@type': type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb 41 | - name: envoy.filters.http.cors 42 | typed_config: 43 | '@type': type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors 44 | - name: envoy.filters.http.router 45 | typed_config: 46 | '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router 47 | clusters: 48 | - name: stocks_service 49 | connect_timeout: 0.25s 50 | type: logical_dns 51 | http2_protocol_options: {} 52 | lb_policy: round_robin 53 | # win/mac hosts: Use address: host.docker.internal instead of address: localhost in the line below 54 | load_assignment: 55 | cluster_name: cluster_0 56 | endpoints: 57 | - lb_endpoints: 58 | - endpoint: 59 | address: 60 | socket_address: 61 | # address: 0.0.0.0 62 | address: host.docker.internal 63 | port_value: 3000 64 | -------------------------------------------------------------------------------- /example/frontend_react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend_react", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "webpack serve", 8 | "protoc": "protoc -I=protos stocks.proto --js_out=import_style=commonjs,binary:protos --grpc-web_out=import_style=commonjs+dts,mode=grpcweb:protos", 9 | "concepts": "ts-node concepts/extendClass.ts" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@emotion/react": "^11.11.1", 15 | "@emotion/styled": "^11.11.0", 16 | "@grpcexpress/usegrpcexpress": "^0.2.6", 17 | "@mui/material": "^5.14.8", 18 | "@mui/x-data-grid": "^6.13.0", 19 | "google-protobuf": "^3.21.2", 20 | "grpc-web": "^1.4.2", 21 | "react": "^18.2.0", 22 | "react-dom": "^18.2.0" 23 | }, 24 | "devDependencies": { 25 | "@types/react": "^18.2.21", 26 | "@types/react-dom": "^18.2.7", 27 | "esbuild-loader": "^4.0.1", 28 | "html-webpack-plugin": "^5.5.3", 29 | "ts-node": "^10.9.1", 30 | "typescript": "^5.2.2", 31 | "webpack": "^5.88.2", 32 | "webpack-cli": "^5.1.4", 33 | "webpack-dev-server": "^4.15.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/frontend_react/protos/StocksServiceClientPb.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview gRPC-Web generated client stub for stocks 3 | * @enhanceable 4 | * @public 5 | */ 6 | 7 | // Code generated by protoc-gen-grpc-web. DO NOT EDIT. 8 | // versions: 9 | // protoc-gen-grpc-web v1.4.2 10 | // protoc v3.20.3 11 | // source: stocks.proto 12 | 13 | 14 | /* eslint-disable */ 15 | // @ts-nocheck 16 | 17 | 18 | import * as grpcWeb from 'grpc-web'; 19 | 20 | import * as stocks_pb from './stocks_pb'; 21 | 22 | 23 | export class StocksServiceClient { 24 | client_: grpcWeb.AbstractClientBase; 25 | hostname_: string; 26 | credentials_: null | { [index: string]: string; }; 27 | options_: null | { [index: string]: any; }; 28 | 29 | constructor (hostname: string, 30 | credentials?: null | { [index: string]: string; }, 31 | options?: null | { [index: string]: any; }) { 32 | if (!options) options = {}; 33 | if (!credentials) credentials = {}; 34 | options['format'] = 'binary'; 35 | 36 | this.client_ = new grpcWeb.GrpcWebClientBase(options); 37 | this.hostname_ = hostname.replace(/\/+$/, ''); 38 | this.credentials_ = credentials; 39 | this.options_ = options; 40 | } 41 | 42 | methodDescriptorGetStocks = new grpcWeb.MethodDescriptor( 43 | '/stocks.StocksService/GetStocks', 44 | grpcWeb.MethodType.UNARY, 45 | stocks_pb.User, 46 | stocks_pb.StocksList, 47 | (request: stocks_pb.User) => { 48 | return request.serializeBinary(); 49 | }, 50 | stocks_pb.StocksList.deserializeBinary 51 | ); 52 | 53 | getStocks( 54 | request: stocks_pb.User, 55 | metadata: grpcWeb.Metadata | null): Promise; 56 | 57 | getStocks( 58 | request: stocks_pb.User, 59 | metadata: grpcWeb.Metadata | null, 60 | callback: (err: grpcWeb.RpcError, 61 | response: stocks_pb.StocksList) => void): grpcWeb.ClientReadableStream; 62 | 63 | getStocks( 64 | request: stocks_pb.User, 65 | metadata: grpcWeb.Metadata | null, 66 | callback?: (err: grpcWeb.RpcError, 67 | response: stocks_pb.StocksList) => void) { 68 | if (callback !== undefined) { 69 | return this.client_.rpcCall( 70 | this.hostname_ + 71 | '/stocks.StocksService/GetStocks', 72 | request, 73 | metadata || {}, 74 | this.methodDescriptorGetStocks, 75 | callback); 76 | } 77 | return this.client_.unaryCall( 78 | this.hostname_ + 79 | '/stocks.StocksService/GetStocks', 80 | request, 81 | metadata || {}, 82 | this.methodDescriptorGetStocks); 83 | } 84 | 85 | methodDescriptorAddStock = new grpcWeb.MethodDescriptor( 86 | '/stocks.StocksService/AddStock', 87 | grpcWeb.MethodType.UNARY, 88 | stocks_pb.UserStock, 89 | stocks_pb.StocksList, 90 | (request: stocks_pb.UserStock) => { 91 | return request.serializeBinary(); 92 | }, 93 | stocks_pb.StocksList.deserializeBinary 94 | ); 95 | 96 | addStock( 97 | request: stocks_pb.UserStock, 98 | metadata: grpcWeb.Metadata | null): Promise; 99 | 100 | addStock( 101 | request: stocks_pb.UserStock, 102 | metadata: grpcWeb.Metadata | null, 103 | callback: (err: grpcWeb.RpcError, 104 | response: stocks_pb.StocksList) => void): grpcWeb.ClientReadableStream; 105 | 106 | addStock( 107 | request: stocks_pb.UserStock, 108 | metadata: grpcWeb.Metadata | null, 109 | callback?: (err: grpcWeb.RpcError, 110 | response: stocks_pb.StocksList) => void) { 111 | if (callback !== undefined) { 112 | return this.client_.rpcCall( 113 | this.hostname_ + 114 | '/stocks.StocksService/AddStock', 115 | request, 116 | metadata || {}, 117 | this.methodDescriptorAddStock, 118 | callback); 119 | } 120 | return this.client_.unaryCall( 121 | this.hostname_ + 122 | '/stocks.StocksService/AddStock', 123 | request, 124 | metadata || {}, 125 | this.methodDescriptorAddStock); 126 | } 127 | 128 | methodDescriptorDeleteStock = new grpcWeb.MethodDescriptor( 129 | '/stocks.StocksService/DeleteStock', 130 | grpcWeb.MethodType.UNARY, 131 | stocks_pb.UserStock, 132 | stocks_pb.StocksList, 133 | (request: stocks_pb.UserStock) => { 134 | return request.serializeBinary(); 135 | }, 136 | stocks_pb.StocksList.deserializeBinary 137 | ); 138 | 139 | deleteStock( 140 | request: stocks_pb.UserStock, 141 | metadata: grpcWeb.Metadata | null): Promise; 142 | 143 | deleteStock( 144 | request: stocks_pb.UserStock, 145 | metadata: grpcWeb.Metadata | null, 146 | callback: (err: grpcWeb.RpcError, 147 | response: stocks_pb.StocksList) => void): grpcWeb.ClientReadableStream; 148 | 149 | deleteStock( 150 | request: stocks_pb.UserStock, 151 | metadata: grpcWeb.Metadata | null, 152 | callback?: (err: grpcWeb.RpcError, 153 | response: stocks_pb.StocksList) => void) { 154 | if (callback !== undefined) { 155 | return this.client_.rpcCall( 156 | this.hostname_ + 157 | '/stocks.StocksService/DeleteStock', 158 | request, 159 | metadata || {}, 160 | this.methodDescriptorDeleteStock, 161 | callback); 162 | } 163 | return this.client_.unaryCall( 164 | this.hostname_ + 165 | '/stocks.StocksService/DeleteStock', 166 | request, 167 | metadata || {}, 168 | this.methodDescriptorDeleteStock); 169 | } 170 | 171 | } 172 | 173 | -------------------------------------------------------------------------------- /example/frontend_react/protos/stocks.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package stocks; 4 | 5 | service StocksService { 6 | rpc GetStocks (User) returns (StocksList) {} 7 | rpc AddStock (UserStock) returns (StocksList) {} 8 | rpc DeleteStock (UserStock) returns (StocksList) {} 9 | } 10 | 11 | message Empty {} 12 | 13 | message StocksList { 14 | repeated Stock stocks = 1; 15 | } 16 | 17 | message Stock { 18 | string symbol = 1; 19 | string name = 2; 20 | float price = 3; 21 | string time = 4; 22 | } 23 | 24 | message StockSymbol { 25 | string symbol = 1; 26 | } 27 | 28 | message User { 29 | string username = 1; 30 | } 31 | 32 | message UserStock { 33 | string username = 1; 34 | repeated string symbol = 2; 35 | } -------------------------------------------------------------------------------- /example/frontend_react/protos/stocks_grpc_web_pb.d.ts: -------------------------------------------------------------------------------- 1 | import * as grpcWeb from 'grpc-web'; 2 | 3 | import * as stocks_pb from './stocks_pb'; 4 | 5 | 6 | export class StocksServiceClient { 7 | constructor (hostname: string, 8 | credentials?: null | { [index: string]: string; }, 9 | options?: null | { [index: string]: any; }); 10 | 11 | getStocks( 12 | request: stocks_pb.User, 13 | metadata: grpcWeb.Metadata | undefined, 14 | callback: (err: grpcWeb.RpcError, 15 | response: stocks_pb.StocksList) => void 16 | ): grpcWeb.ClientReadableStream; 17 | 18 | addStock( 19 | request: stocks_pb.UserStock, 20 | metadata: grpcWeb.Metadata | undefined, 21 | callback: (err: grpcWeb.RpcError, 22 | response: stocks_pb.StocksList) => void 23 | ): grpcWeb.ClientReadableStream; 24 | 25 | deleteStock( 26 | request: stocks_pb.UserStock, 27 | metadata: grpcWeb.Metadata | undefined, 28 | callback: (err: grpcWeb.RpcError, 29 | response: stocks_pb.StocksList) => void 30 | ): grpcWeb.ClientReadableStream; 31 | 32 | } 33 | 34 | export class StocksServicePromiseClient { 35 | constructor (hostname: string, 36 | credentials?: null | { [index: string]: string; }, 37 | options?: null | { [index: string]: any; }); 38 | 39 | getStocks( 40 | request: stocks_pb.User, 41 | metadata?: grpcWeb.Metadata 42 | ): Promise; 43 | 44 | addStock( 45 | request: stocks_pb.UserStock, 46 | metadata?: grpcWeb.Metadata 47 | ): Promise; 48 | 49 | deleteStock( 50 | request: stocks_pb.UserStock, 51 | metadata?: grpcWeb.Metadata 52 | ): Promise; 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /example/frontend_react/protos/stocks_grpc_web_pb.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview gRPC-Web generated client stub for stocks 3 | * @enhanceable 4 | * @public 5 | */ 6 | 7 | // Code generated by protoc-gen-grpc-web. DO NOT EDIT. 8 | // versions: 9 | // protoc-gen-grpc-web v1.4.2 10 | // protoc v3.20.3 11 | // source: stocks.proto 12 | 13 | 14 | /* eslint-disable */ 15 | // @ts-nocheck 16 | 17 | 18 | 19 | const grpc = {}; 20 | grpc.web = require('grpc-web'); 21 | 22 | const proto = {}; 23 | proto.stocks = require('./stocks_pb.js'); 24 | 25 | /** 26 | * @param {string} hostname 27 | * @param {?Object} credentials 28 | * @param {?grpc.web.ClientOptions} options 29 | * @constructor 30 | * @struct 31 | * @final 32 | */ 33 | proto.stocks.StocksServiceClient = 34 | function(hostname, credentials, options) { 35 | if (!options) options = {}; 36 | options.format = 'binary'; 37 | 38 | /** 39 | * @private @const {!grpc.web.GrpcWebClientBase} The client 40 | */ 41 | this.client_ = new grpc.web.GrpcWebClientBase(options); 42 | 43 | /** 44 | * @private @const {string} The hostname 45 | */ 46 | this.hostname_ = hostname.replace(/\/+$/, ''); 47 | 48 | }; 49 | 50 | 51 | /** 52 | * @param {string} hostname 53 | * @param {?Object} credentials 54 | * @param {?grpc.web.ClientOptions} options 55 | * @constructor 56 | * @struct 57 | * @final 58 | */ 59 | proto.stocks.StocksServicePromiseClient = 60 | function(hostname, credentials, options) { 61 | if (!options) options = {}; 62 | options.format = 'binary'; 63 | 64 | /** 65 | * @private @const {!grpc.web.GrpcWebClientBase} The client 66 | */ 67 | this.client_ = new grpc.web.GrpcWebClientBase(options); 68 | 69 | /** 70 | * @private @const {string} The hostname 71 | */ 72 | this.hostname_ = hostname.replace(/\/+$/, ''); 73 | 74 | }; 75 | 76 | 77 | /** 78 | * @const 79 | * @type {!grpc.web.MethodDescriptor< 80 | * !proto.stocks.User, 81 | * !proto.stocks.StocksList>} 82 | */ 83 | const methodDescriptor_StocksService_GetStocks = new grpc.web.MethodDescriptor( 84 | '/stocks.StocksService/GetStocks', 85 | grpc.web.MethodType.UNARY, 86 | proto.stocks.User, 87 | proto.stocks.StocksList, 88 | /** 89 | * @param {!proto.stocks.User} request 90 | * @return {!Uint8Array} 91 | */ 92 | function(request) { 93 | return request.serializeBinary(); 94 | }, 95 | proto.stocks.StocksList.deserializeBinary 96 | ); 97 | 98 | 99 | /** 100 | * @param {!proto.stocks.User} request The 101 | * request proto 102 | * @param {?Object} metadata User defined 103 | * call metadata 104 | * @param {function(?grpc.web.RpcError, ?proto.stocks.StocksList)} 105 | * callback The callback function(error, response) 106 | * @return {!grpc.web.ClientReadableStream|undefined} 107 | * The XHR Node Readable Stream 108 | */ 109 | proto.stocks.StocksServiceClient.prototype.getStocks = 110 | function(request, metadata, callback) { 111 | return this.client_.rpcCall(this.hostname_ + 112 | '/stocks.StocksService/GetStocks', 113 | request, 114 | metadata || {}, 115 | methodDescriptor_StocksService_GetStocks, 116 | callback); 117 | }; 118 | 119 | 120 | /** 121 | * @param {!proto.stocks.User} request The 122 | * request proto 123 | * @param {?Object=} metadata User defined 124 | * call metadata 125 | * @return {!Promise} 126 | * Promise that resolves to the response 127 | */ 128 | proto.stocks.StocksServicePromiseClient.prototype.getStocks = 129 | function(request, metadata) { 130 | return this.client_.unaryCall(this.hostname_ + 131 | '/stocks.StocksService/GetStocks', 132 | request, 133 | metadata || {}, 134 | methodDescriptor_StocksService_GetStocks); 135 | }; 136 | 137 | 138 | /** 139 | * @const 140 | * @type {!grpc.web.MethodDescriptor< 141 | * !proto.stocks.UserStock, 142 | * !proto.stocks.StocksList>} 143 | */ 144 | const methodDescriptor_StocksService_AddStock = new grpc.web.MethodDescriptor( 145 | '/stocks.StocksService/AddStock', 146 | grpc.web.MethodType.UNARY, 147 | proto.stocks.UserStock, 148 | proto.stocks.StocksList, 149 | /** 150 | * @param {!proto.stocks.UserStock} request 151 | * @return {!Uint8Array} 152 | */ 153 | function(request) { 154 | return request.serializeBinary(); 155 | }, 156 | proto.stocks.StocksList.deserializeBinary 157 | ); 158 | 159 | 160 | /** 161 | * @param {!proto.stocks.UserStock} request The 162 | * request proto 163 | * @param {?Object} metadata User defined 164 | * call metadata 165 | * @param {function(?grpc.web.RpcError, ?proto.stocks.StocksList)} 166 | * callback The callback function(error, response) 167 | * @return {!grpc.web.ClientReadableStream|undefined} 168 | * The XHR Node Readable Stream 169 | */ 170 | proto.stocks.StocksServiceClient.prototype.addStock = 171 | function(request, metadata, callback) { 172 | return this.client_.rpcCall(this.hostname_ + 173 | '/stocks.StocksService/AddStock', 174 | request, 175 | metadata || {}, 176 | methodDescriptor_StocksService_AddStock, 177 | callback); 178 | }; 179 | 180 | 181 | /** 182 | * @param {!proto.stocks.UserStock} request The 183 | * request proto 184 | * @param {?Object=} metadata User defined 185 | * call metadata 186 | * @return {!Promise} 187 | * Promise that resolves to the response 188 | */ 189 | proto.stocks.StocksServicePromiseClient.prototype.addStock = 190 | function(request, metadata) { 191 | return this.client_.unaryCall(this.hostname_ + 192 | '/stocks.StocksService/AddStock', 193 | request, 194 | metadata || {}, 195 | methodDescriptor_StocksService_AddStock); 196 | }; 197 | 198 | 199 | /** 200 | * @const 201 | * @type {!grpc.web.MethodDescriptor< 202 | * !proto.stocks.UserStock, 203 | * !proto.stocks.StocksList>} 204 | */ 205 | const methodDescriptor_StocksService_DeleteStock = new grpc.web.MethodDescriptor( 206 | '/stocks.StocksService/DeleteStock', 207 | grpc.web.MethodType.UNARY, 208 | proto.stocks.UserStock, 209 | proto.stocks.StocksList, 210 | /** 211 | * @param {!proto.stocks.UserStock} request 212 | * @return {!Uint8Array} 213 | */ 214 | function(request) { 215 | return request.serializeBinary(); 216 | }, 217 | proto.stocks.StocksList.deserializeBinary 218 | ); 219 | 220 | 221 | /** 222 | * @param {!proto.stocks.UserStock} request The 223 | * request proto 224 | * @param {?Object} metadata User defined 225 | * call metadata 226 | * @param {function(?grpc.web.RpcError, ?proto.stocks.StocksList)} 227 | * callback The callback function(error, response) 228 | * @return {!grpc.web.ClientReadableStream|undefined} 229 | * The XHR Node Readable Stream 230 | */ 231 | proto.stocks.StocksServiceClient.prototype.deleteStock = 232 | function(request, metadata, callback) { 233 | return this.client_.rpcCall(this.hostname_ + 234 | '/stocks.StocksService/DeleteStock', 235 | request, 236 | metadata || {}, 237 | methodDescriptor_StocksService_DeleteStock, 238 | callback); 239 | }; 240 | 241 | 242 | /** 243 | * @param {!proto.stocks.UserStock} request The 244 | * request proto 245 | * @param {?Object=} metadata User defined 246 | * call metadata 247 | * @return {!Promise} 248 | * Promise that resolves to the response 249 | */ 250 | proto.stocks.StocksServicePromiseClient.prototype.deleteStock = 251 | function(request, metadata) { 252 | return this.client_.unaryCall(this.hostname_ + 253 | '/stocks.StocksService/DeleteStock', 254 | request, 255 | metadata || {}, 256 | methodDescriptor_StocksService_DeleteStock); 257 | }; 258 | 259 | 260 | module.exports = proto.stocks; 261 | 262 | -------------------------------------------------------------------------------- /example/frontend_react/protos/stocks_pb.d.ts: -------------------------------------------------------------------------------- 1 | import * as jspb from 'google-protobuf' 2 | 3 | 4 | 5 | export class Empty extends jspb.Message { 6 | serializeBinary(): Uint8Array; 7 | toObject(includeInstance?: boolean): Empty.AsObject; 8 | static toObject(includeInstance: boolean, msg: Empty): Empty.AsObject; 9 | static serializeBinaryToWriter(message: Empty, writer: jspb.BinaryWriter): void; 10 | static deserializeBinary(bytes: Uint8Array): Empty; 11 | static deserializeBinaryFromReader(message: Empty, reader: jspb.BinaryReader): Empty; 12 | } 13 | 14 | export namespace Empty { 15 | export type AsObject = { 16 | } 17 | } 18 | 19 | export class StocksList extends jspb.Message { 20 | getStocksList(): Array; 21 | setStocksList(value: Array): StocksList; 22 | clearStocksList(): StocksList; 23 | addStocks(value?: Stock, index?: number): Stock; 24 | 25 | serializeBinary(): Uint8Array; 26 | toObject(includeInstance?: boolean): StocksList.AsObject; 27 | static toObject(includeInstance: boolean, msg: StocksList): StocksList.AsObject; 28 | static serializeBinaryToWriter(message: StocksList, writer: jspb.BinaryWriter): void; 29 | static deserializeBinary(bytes: Uint8Array): StocksList; 30 | static deserializeBinaryFromReader(message: StocksList, reader: jspb.BinaryReader): StocksList; 31 | } 32 | 33 | export namespace StocksList { 34 | export type AsObject = { 35 | stocksList: Array, 36 | } 37 | } 38 | 39 | export class Stock extends jspb.Message { 40 | getSymbol(): string; 41 | setSymbol(value: string): Stock; 42 | 43 | getName(): string; 44 | setName(value: string): Stock; 45 | 46 | getPrice(): number; 47 | setPrice(value: number): Stock; 48 | 49 | getTime(): string; 50 | setTime(value: string): Stock; 51 | 52 | serializeBinary(): Uint8Array; 53 | toObject(includeInstance?: boolean): Stock.AsObject; 54 | static toObject(includeInstance: boolean, msg: Stock): Stock.AsObject; 55 | static serializeBinaryToWriter(message: Stock, writer: jspb.BinaryWriter): void; 56 | static deserializeBinary(bytes: Uint8Array): Stock; 57 | static deserializeBinaryFromReader(message: Stock, reader: jspb.BinaryReader): Stock; 58 | } 59 | 60 | export namespace Stock { 61 | export type AsObject = { 62 | symbol: string, 63 | name: string, 64 | price: number, 65 | time: string, 66 | } 67 | } 68 | 69 | export class StockSymbol extends jspb.Message { 70 | getSymbol(): string; 71 | setSymbol(value: string): StockSymbol; 72 | 73 | serializeBinary(): Uint8Array; 74 | toObject(includeInstance?: boolean): StockSymbol.AsObject; 75 | static toObject(includeInstance: boolean, msg: StockSymbol): StockSymbol.AsObject; 76 | static serializeBinaryToWriter(message: StockSymbol, writer: jspb.BinaryWriter): void; 77 | static deserializeBinary(bytes: Uint8Array): StockSymbol; 78 | static deserializeBinaryFromReader(message: StockSymbol, reader: jspb.BinaryReader): StockSymbol; 79 | } 80 | 81 | export namespace StockSymbol { 82 | export type AsObject = { 83 | symbol: string, 84 | } 85 | } 86 | 87 | export class User extends jspb.Message { 88 | getUsername(): string; 89 | setUsername(value: string): User; 90 | 91 | serializeBinary(): Uint8Array; 92 | toObject(includeInstance?: boolean): User.AsObject; 93 | static toObject(includeInstance: boolean, msg: User): User.AsObject; 94 | static serializeBinaryToWriter(message: User, writer: jspb.BinaryWriter): void; 95 | static deserializeBinary(bytes: Uint8Array): User; 96 | static deserializeBinaryFromReader(message: User, reader: jspb.BinaryReader): User; 97 | } 98 | 99 | export namespace User { 100 | export type AsObject = { 101 | username: string, 102 | } 103 | } 104 | 105 | export class UserStock extends jspb.Message { 106 | getUsername(): string; 107 | setUsername(value: string): UserStock; 108 | 109 | getSymbolList(): Array; 110 | setSymbolList(value: Array): UserStock; 111 | clearSymbolList(): UserStock; 112 | addSymbol(value: string, index?: number): UserStock; 113 | 114 | serializeBinary(): Uint8Array; 115 | toObject(includeInstance?: boolean): UserStock.AsObject; 116 | static toObject(includeInstance: boolean, msg: UserStock): UserStock.AsObject; 117 | static serializeBinaryToWriter(message: UserStock, writer: jspb.BinaryWriter): void; 118 | static deserializeBinary(bytes: Uint8Array): UserStock; 119 | static deserializeBinaryFromReader(message: UserStock, reader: jspb.BinaryReader): UserStock; 120 | } 121 | 122 | export namespace UserStock { 123 | export type AsObject = { 124 | username: string, 125 | symbolList: Array, 126 | } 127 | } 128 | 129 | -------------------------------------------------------------------------------- /example/frontend_react/protos/stocks_pb.js: -------------------------------------------------------------------------------- 1 | // source: stocks.proto 2 | /** 3 | * @fileoverview 4 | * @enhanceable 5 | * @suppress {missingRequire} reports error on implicit type usages. 6 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 7 | * field starts with 'MSG_' and isn't a translatable message. 8 | * @public 9 | */ 10 | // GENERATED CODE -- DO NOT EDIT! 11 | /* eslint-disable */ 12 | // @ts-nocheck 13 | 14 | var jspb = require('google-protobuf'); 15 | var goog = jspb; 16 | var global = (function() { return this || window || global || self || Function('return this')(); }).call(null); 17 | 18 | goog.exportSymbol('proto.stocks.Empty', null, global); 19 | goog.exportSymbol('proto.stocks.Stock', null, global); 20 | goog.exportSymbol('proto.stocks.StockSymbol', null, global); 21 | goog.exportSymbol('proto.stocks.StocksList', null, global); 22 | goog.exportSymbol('proto.stocks.User', null, global); 23 | goog.exportSymbol('proto.stocks.UserStock', null, global); 24 | /** 25 | * Generated by JsPbCodeGenerator. 26 | * @param {Array=} opt_data Optional initial data array, typically from a 27 | * server response, or constructed directly in Javascript. The array is used 28 | * in place and becomes part of the constructed object. It is not cloned. 29 | * If no data is provided, the constructed object will be empty, but still 30 | * valid. 31 | * @extends {jspb.Message} 32 | * @constructor 33 | */ 34 | proto.stocks.Empty = function(opt_data) { 35 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 36 | }; 37 | goog.inherits(proto.stocks.Empty, jspb.Message); 38 | if (goog.DEBUG && !COMPILED) { 39 | /** 40 | * @public 41 | * @override 42 | */ 43 | proto.stocks.Empty.displayName = 'proto.stocks.Empty'; 44 | } 45 | /** 46 | * Generated by JsPbCodeGenerator. 47 | * @param {Array=} opt_data Optional initial data array, typically from a 48 | * server response, or constructed directly in Javascript. The array is used 49 | * in place and becomes part of the constructed object. It is not cloned. 50 | * If no data is provided, the constructed object will be empty, but still 51 | * valid. 52 | * @extends {jspb.Message} 53 | * @constructor 54 | */ 55 | proto.stocks.StocksList = function(opt_data) { 56 | jspb.Message.initialize(this, opt_data, 0, -1, proto.stocks.StocksList.repeatedFields_, null); 57 | }; 58 | goog.inherits(proto.stocks.StocksList, jspb.Message); 59 | if (goog.DEBUG && !COMPILED) { 60 | /** 61 | * @public 62 | * @override 63 | */ 64 | proto.stocks.StocksList.displayName = 'proto.stocks.StocksList'; 65 | } 66 | /** 67 | * Generated by JsPbCodeGenerator. 68 | * @param {Array=} opt_data Optional initial data array, typically from a 69 | * server response, or constructed directly in Javascript. The array is used 70 | * in place and becomes part of the constructed object. It is not cloned. 71 | * If no data is provided, the constructed object will be empty, but still 72 | * valid. 73 | * @extends {jspb.Message} 74 | * @constructor 75 | */ 76 | proto.stocks.Stock = function(opt_data) { 77 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 78 | }; 79 | goog.inherits(proto.stocks.Stock, jspb.Message); 80 | if (goog.DEBUG && !COMPILED) { 81 | /** 82 | * @public 83 | * @override 84 | */ 85 | proto.stocks.Stock.displayName = 'proto.stocks.Stock'; 86 | } 87 | /** 88 | * Generated by JsPbCodeGenerator. 89 | * @param {Array=} opt_data Optional initial data array, typically from a 90 | * server response, or constructed directly in Javascript. The array is used 91 | * in place and becomes part of the constructed object. It is not cloned. 92 | * If no data is provided, the constructed object will be empty, but still 93 | * valid. 94 | * @extends {jspb.Message} 95 | * @constructor 96 | */ 97 | proto.stocks.StockSymbol = function(opt_data) { 98 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 99 | }; 100 | goog.inherits(proto.stocks.StockSymbol, jspb.Message); 101 | if (goog.DEBUG && !COMPILED) { 102 | /** 103 | * @public 104 | * @override 105 | */ 106 | proto.stocks.StockSymbol.displayName = 'proto.stocks.StockSymbol'; 107 | } 108 | /** 109 | * Generated by JsPbCodeGenerator. 110 | * @param {Array=} opt_data Optional initial data array, typically from a 111 | * server response, or constructed directly in Javascript. The array is used 112 | * in place and becomes part of the constructed object. It is not cloned. 113 | * If no data is provided, the constructed object will be empty, but still 114 | * valid. 115 | * @extends {jspb.Message} 116 | * @constructor 117 | */ 118 | proto.stocks.User = function(opt_data) { 119 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 120 | }; 121 | goog.inherits(proto.stocks.User, jspb.Message); 122 | if (goog.DEBUG && !COMPILED) { 123 | /** 124 | * @public 125 | * @override 126 | */ 127 | proto.stocks.User.displayName = 'proto.stocks.User'; 128 | } 129 | /** 130 | * Generated by JsPbCodeGenerator. 131 | * @param {Array=} opt_data Optional initial data array, typically from a 132 | * server response, or constructed directly in Javascript. The array is used 133 | * in place and becomes part of the constructed object. It is not cloned. 134 | * If no data is provided, the constructed object will be empty, but still 135 | * valid. 136 | * @extends {jspb.Message} 137 | * @constructor 138 | */ 139 | proto.stocks.UserStock = function(opt_data) { 140 | jspb.Message.initialize(this, opt_data, 0, -1, proto.stocks.UserStock.repeatedFields_, null); 141 | }; 142 | goog.inherits(proto.stocks.UserStock, jspb.Message); 143 | if (goog.DEBUG && !COMPILED) { 144 | /** 145 | * @public 146 | * @override 147 | */ 148 | proto.stocks.UserStock.displayName = 'proto.stocks.UserStock'; 149 | } 150 | 151 | 152 | 153 | if (jspb.Message.GENERATE_TO_OBJECT) { 154 | /** 155 | * Creates an object representation of this proto. 156 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 157 | * Optional fields that are not set will be set to undefined. 158 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 159 | * For the list of reserved names please see: 160 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 161 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 162 | * JSPB instance for transitional soy proto support: 163 | * http://goto/soy-param-migration 164 | * @return {!Object} 165 | */ 166 | proto.stocks.Empty.prototype.toObject = function(opt_includeInstance) { 167 | return proto.stocks.Empty.toObject(opt_includeInstance, this); 168 | }; 169 | 170 | 171 | /** 172 | * Static version of the {@see toObject} method. 173 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 174 | * the JSPB instance for transitional soy proto support: 175 | * http://goto/soy-param-migration 176 | * @param {!proto.stocks.Empty} msg The msg instance to transform. 177 | * @return {!Object} 178 | * @suppress {unusedLocalVariables} f is only used for nested messages 179 | */ 180 | proto.stocks.Empty.toObject = function(includeInstance, msg) { 181 | var f, obj = { 182 | 183 | }; 184 | 185 | if (includeInstance) { 186 | obj.$jspbMessageInstance = msg; 187 | } 188 | return obj; 189 | }; 190 | } 191 | 192 | 193 | /** 194 | * Deserializes binary data (in protobuf wire format). 195 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 196 | * @return {!proto.stocks.Empty} 197 | */ 198 | proto.stocks.Empty.deserializeBinary = function(bytes) { 199 | var reader = new jspb.BinaryReader(bytes); 200 | var msg = new proto.stocks.Empty; 201 | return proto.stocks.Empty.deserializeBinaryFromReader(msg, reader); 202 | }; 203 | 204 | 205 | /** 206 | * Deserializes binary data (in protobuf wire format) from the 207 | * given reader into the given message object. 208 | * @param {!proto.stocks.Empty} msg The message object to deserialize into. 209 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 210 | * @return {!proto.stocks.Empty} 211 | */ 212 | proto.stocks.Empty.deserializeBinaryFromReader = function(msg, reader) { 213 | while (reader.nextField()) { 214 | if (reader.isEndGroup()) { 215 | break; 216 | } 217 | var field = reader.getFieldNumber(); 218 | switch (field) { 219 | default: 220 | reader.skipField(); 221 | break; 222 | } 223 | } 224 | return msg; 225 | }; 226 | 227 | 228 | /** 229 | * Serializes the message to binary data (in protobuf wire format). 230 | * @return {!Uint8Array} 231 | */ 232 | proto.stocks.Empty.prototype.serializeBinary = function() { 233 | var writer = new jspb.BinaryWriter(); 234 | proto.stocks.Empty.serializeBinaryToWriter(this, writer); 235 | return writer.getResultBuffer(); 236 | }; 237 | 238 | 239 | /** 240 | * Serializes the given message to binary data (in protobuf wire 241 | * format), writing to the given BinaryWriter. 242 | * @param {!proto.stocks.Empty} message 243 | * @param {!jspb.BinaryWriter} writer 244 | * @suppress {unusedLocalVariables} f is only used for nested messages 245 | */ 246 | proto.stocks.Empty.serializeBinaryToWriter = function(message, writer) { 247 | var f = undefined; 248 | }; 249 | 250 | 251 | 252 | /** 253 | * List of repeated fields within this message type. 254 | * @private {!Array} 255 | * @const 256 | */ 257 | proto.stocks.StocksList.repeatedFields_ = [1]; 258 | 259 | 260 | 261 | if (jspb.Message.GENERATE_TO_OBJECT) { 262 | /** 263 | * Creates an object representation of this proto. 264 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 265 | * Optional fields that are not set will be set to undefined. 266 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 267 | * For the list of reserved names please see: 268 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 269 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 270 | * JSPB instance for transitional soy proto support: 271 | * http://goto/soy-param-migration 272 | * @return {!Object} 273 | */ 274 | proto.stocks.StocksList.prototype.toObject = function(opt_includeInstance) { 275 | return proto.stocks.StocksList.toObject(opt_includeInstance, this); 276 | }; 277 | 278 | 279 | /** 280 | * Static version of the {@see toObject} method. 281 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 282 | * the JSPB instance for transitional soy proto support: 283 | * http://goto/soy-param-migration 284 | * @param {!proto.stocks.StocksList} msg The msg instance to transform. 285 | * @return {!Object} 286 | * @suppress {unusedLocalVariables} f is only used for nested messages 287 | */ 288 | proto.stocks.StocksList.toObject = function(includeInstance, msg) { 289 | var f, obj = { 290 | stocksList: jspb.Message.toObjectList(msg.getStocksList(), 291 | proto.stocks.Stock.toObject, includeInstance) 292 | }; 293 | 294 | if (includeInstance) { 295 | obj.$jspbMessageInstance = msg; 296 | } 297 | return obj; 298 | }; 299 | } 300 | 301 | 302 | /** 303 | * Deserializes binary data (in protobuf wire format). 304 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 305 | * @return {!proto.stocks.StocksList} 306 | */ 307 | proto.stocks.StocksList.deserializeBinary = function(bytes) { 308 | var reader = new jspb.BinaryReader(bytes); 309 | var msg = new proto.stocks.StocksList; 310 | return proto.stocks.StocksList.deserializeBinaryFromReader(msg, reader); 311 | }; 312 | 313 | 314 | /** 315 | * Deserializes binary data (in protobuf wire format) from the 316 | * given reader into the given message object. 317 | * @param {!proto.stocks.StocksList} msg The message object to deserialize into. 318 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 319 | * @return {!proto.stocks.StocksList} 320 | */ 321 | proto.stocks.StocksList.deserializeBinaryFromReader = function(msg, reader) { 322 | while (reader.nextField()) { 323 | if (reader.isEndGroup()) { 324 | break; 325 | } 326 | var field = reader.getFieldNumber(); 327 | switch (field) { 328 | case 1: 329 | var value = new proto.stocks.Stock; 330 | reader.readMessage(value,proto.stocks.Stock.deserializeBinaryFromReader); 331 | msg.addStocks(value); 332 | break; 333 | default: 334 | reader.skipField(); 335 | break; 336 | } 337 | } 338 | return msg; 339 | }; 340 | 341 | 342 | /** 343 | * Serializes the message to binary data (in protobuf wire format). 344 | * @return {!Uint8Array} 345 | */ 346 | proto.stocks.StocksList.prototype.serializeBinary = function() { 347 | var writer = new jspb.BinaryWriter(); 348 | proto.stocks.StocksList.serializeBinaryToWriter(this, writer); 349 | return writer.getResultBuffer(); 350 | }; 351 | 352 | 353 | /** 354 | * Serializes the given message to binary data (in protobuf wire 355 | * format), writing to the given BinaryWriter. 356 | * @param {!proto.stocks.StocksList} message 357 | * @param {!jspb.BinaryWriter} writer 358 | * @suppress {unusedLocalVariables} f is only used for nested messages 359 | */ 360 | proto.stocks.StocksList.serializeBinaryToWriter = function(message, writer) { 361 | var f = undefined; 362 | f = message.getStocksList(); 363 | if (f.length > 0) { 364 | writer.writeRepeatedMessage( 365 | 1, 366 | f, 367 | proto.stocks.Stock.serializeBinaryToWriter 368 | ); 369 | } 370 | }; 371 | 372 | 373 | /** 374 | * repeated Stock stocks = 1; 375 | * @return {!Array} 376 | */ 377 | proto.stocks.StocksList.prototype.getStocksList = function() { 378 | return /** @type{!Array} */ ( 379 | jspb.Message.getRepeatedWrapperField(this, proto.stocks.Stock, 1)); 380 | }; 381 | 382 | 383 | /** 384 | * @param {!Array} value 385 | * @return {!proto.stocks.StocksList} returns this 386 | */ 387 | proto.stocks.StocksList.prototype.setStocksList = function(value) { 388 | return jspb.Message.setRepeatedWrapperField(this, 1, value); 389 | }; 390 | 391 | 392 | /** 393 | * @param {!proto.stocks.Stock=} opt_value 394 | * @param {number=} opt_index 395 | * @return {!proto.stocks.Stock} 396 | */ 397 | proto.stocks.StocksList.prototype.addStocks = function(opt_value, opt_index) { 398 | return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.stocks.Stock, opt_index); 399 | }; 400 | 401 | 402 | /** 403 | * Clears the list making it empty but non-null. 404 | * @return {!proto.stocks.StocksList} returns this 405 | */ 406 | proto.stocks.StocksList.prototype.clearStocksList = function() { 407 | return this.setStocksList([]); 408 | }; 409 | 410 | 411 | 412 | 413 | 414 | if (jspb.Message.GENERATE_TO_OBJECT) { 415 | /** 416 | * Creates an object representation of this proto. 417 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 418 | * Optional fields that are not set will be set to undefined. 419 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 420 | * For the list of reserved names please see: 421 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 422 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 423 | * JSPB instance for transitional soy proto support: 424 | * http://goto/soy-param-migration 425 | * @return {!Object} 426 | */ 427 | proto.stocks.Stock.prototype.toObject = function(opt_includeInstance) { 428 | return proto.stocks.Stock.toObject(opt_includeInstance, this); 429 | }; 430 | 431 | 432 | /** 433 | * Static version of the {@see toObject} method. 434 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 435 | * the JSPB instance for transitional soy proto support: 436 | * http://goto/soy-param-migration 437 | * @param {!proto.stocks.Stock} msg The msg instance to transform. 438 | * @return {!Object} 439 | * @suppress {unusedLocalVariables} f is only used for nested messages 440 | */ 441 | proto.stocks.Stock.toObject = function(includeInstance, msg) { 442 | var f, obj = { 443 | symbol: jspb.Message.getFieldWithDefault(msg, 1, ""), 444 | name: jspb.Message.getFieldWithDefault(msg, 2, ""), 445 | price: jspb.Message.getFloatingPointFieldWithDefault(msg, 3, 0.0), 446 | time: jspb.Message.getFieldWithDefault(msg, 4, "") 447 | }; 448 | 449 | if (includeInstance) { 450 | obj.$jspbMessageInstance = msg; 451 | } 452 | return obj; 453 | }; 454 | } 455 | 456 | 457 | /** 458 | * Deserializes binary data (in protobuf wire format). 459 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 460 | * @return {!proto.stocks.Stock} 461 | */ 462 | proto.stocks.Stock.deserializeBinary = function(bytes) { 463 | var reader = new jspb.BinaryReader(bytes); 464 | var msg = new proto.stocks.Stock; 465 | return proto.stocks.Stock.deserializeBinaryFromReader(msg, reader); 466 | }; 467 | 468 | 469 | /** 470 | * Deserializes binary data (in protobuf wire format) from the 471 | * given reader into the given message object. 472 | * @param {!proto.stocks.Stock} msg The message object to deserialize into. 473 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 474 | * @return {!proto.stocks.Stock} 475 | */ 476 | proto.stocks.Stock.deserializeBinaryFromReader = function(msg, reader) { 477 | while (reader.nextField()) { 478 | if (reader.isEndGroup()) { 479 | break; 480 | } 481 | var field = reader.getFieldNumber(); 482 | switch (field) { 483 | case 1: 484 | var value = /** @type {string} */ (reader.readString()); 485 | msg.setSymbol(value); 486 | break; 487 | case 2: 488 | var value = /** @type {string} */ (reader.readString()); 489 | msg.setName(value); 490 | break; 491 | case 3: 492 | var value = /** @type {number} */ (reader.readFloat()); 493 | msg.setPrice(value); 494 | break; 495 | case 4: 496 | var value = /** @type {string} */ (reader.readString()); 497 | msg.setTime(value); 498 | break; 499 | default: 500 | reader.skipField(); 501 | break; 502 | } 503 | } 504 | return msg; 505 | }; 506 | 507 | 508 | /** 509 | * Serializes the message to binary data (in protobuf wire format). 510 | * @return {!Uint8Array} 511 | */ 512 | proto.stocks.Stock.prototype.serializeBinary = function() { 513 | var writer = new jspb.BinaryWriter(); 514 | proto.stocks.Stock.serializeBinaryToWriter(this, writer); 515 | return writer.getResultBuffer(); 516 | }; 517 | 518 | 519 | /** 520 | * Serializes the given message to binary data (in protobuf wire 521 | * format), writing to the given BinaryWriter. 522 | * @param {!proto.stocks.Stock} message 523 | * @param {!jspb.BinaryWriter} writer 524 | * @suppress {unusedLocalVariables} f is only used for nested messages 525 | */ 526 | proto.stocks.Stock.serializeBinaryToWriter = function(message, writer) { 527 | var f = undefined; 528 | f = message.getSymbol(); 529 | if (f.length > 0) { 530 | writer.writeString( 531 | 1, 532 | f 533 | ); 534 | } 535 | f = message.getName(); 536 | if (f.length > 0) { 537 | writer.writeString( 538 | 2, 539 | f 540 | ); 541 | } 542 | f = message.getPrice(); 543 | if (f !== 0.0) { 544 | writer.writeFloat( 545 | 3, 546 | f 547 | ); 548 | } 549 | f = message.getTime(); 550 | if (f.length > 0) { 551 | writer.writeString( 552 | 4, 553 | f 554 | ); 555 | } 556 | }; 557 | 558 | 559 | /** 560 | * optional string symbol = 1; 561 | * @return {string} 562 | */ 563 | proto.stocks.Stock.prototype.getSymbol = function() { 564 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); 565 | }; 566 | 567 | 568 | /** 569 | * @param {string} value 570 | * @return {!proto.stocks.Stock} returns this 571 | */ 572 | proto.stocks.Stock.prototype.setSymbol = function(value) { 573 | return jspb.Message.setProto3StringField(this, 1, value); 574 | }; 575 | 576 | 577 | /** 578 | * optional string name = 2; 579 | * @return {string} 580 | */ 581 | proto.stocks.Stock.prototype.getName = function() { 582 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); 583 | }; 584 | 585 | 586 | /** 587 | * @param {string} value 588 | * @return {!proto.stocks.Stock} returns this 589 | */ 590 | proto.stocks.Stock.prototype.setName = function(value) { 591 | return jspb.Message.setProto3StringField(this, 2, value); 592 | }; 593 | 594 | 595 | /** 596 | * optional float price = 3; 597 | * @return {number} 598 | */ 599 | proto.stocks.Stock.prototype.getPrice = function() { 600 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 3, 0.0)); 601 | }; 602 | 603 | 604 | /** 605 | * @param {number} value 606 | * @return {!proto.stocks.Stock} returns this 607 | */ 608 | proto.stocks.Stock.prototype.setPrice = function(value) { 609 | return jspb.Message.setProto3FloatField(this, 3, value); 610 | }; 611 | 612 | 613 | /** 614 | * optional string time = 4; 615 | * @return {string} 616 | */ 617 | proto.stocks.Stock.prototype.getTime = function() { 618 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); 619 | }; 620 | 621 | 622 | /** 623 | * @param {string} value 624 | * @return {!proto.stocks.Stock} returns this 625 | */ 626 | proto.stocks.Stock.prototype.setTime = function(value) { 627 | return jspb.Message.setProto3StringField(this, 4, value); 628 | }; 629 | 630 | 631 | 632 | 633 | 634 | if (jspb.Message.GENERATE_TO_OBJECT) { 635 | /** 636 | * Creates an object representation of this proto. 637 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 638 | * Optional fields that are not set will be set to undefined. 639 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 640 | * For the list of reserved names please see: 641 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 642 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 643 | * JSPB instance for transitional soy proto support: 644 | * http://goto/soy-param-migration 645 | * @return {!Object} 646 | */ 647 | proto.stocks.StockSymbol.prototype.toObject = function(opt_includeInstance) { 648 | return proto.stocks.StockSymbol.toObject(opt_includeInstance, this); 649 | }; 650 | 651 | 652 | /** 653 | * Static version of the {@see toObject} method. 654 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 655 | * the JSPB instance for transitional soy proto support: 656 | * http://goto/soy-param-migration 657 | * @param {!proto.stocks.StockSymbol} msg The msg instance to transform. 658 | * @return {!Object} 659 | * @suppress {unusedLocalVariables} f is only used for nested messages 660 | */ 661 | proto.stocks.StockSymbol.toObject = function(includeInstance, msg) { 662 | var f, obj = { 663 | symbol: jspb.Message.getFieldWithDefault(msg, 1, "") 664 | }; 665 | 666 | if (includeInstance) { 667 | obj.$jspbMessageInstance = msg; 668 | } 669 | return obj; 670 | }; 671 | } 672 | 673 | 674 | /** 675 | * Deserializes binary data (in protobuf wire format). 676 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 677 | * @return {!proto.stocks.StockSymbol} 678 | */ 679 | proto.stocks.StockSymbol.deserializeBinary = function(bytes) { 680 | var reader = new jspb.BinaryReader(bytes); 681 | var msg = new proto.stocks.StockSymbol; 682 | return proto.stocks.StockSymbol.deserializeBinaryFromReader(msg, reader); 683 | }; 684 | 685 | 686 | /** 687 | * Deserializes binary data (in protobuf wire format) from the 688 | * given reader into the given message object. 689 | * @param {!proto.stocks.StockSymbol} msg The message object to deserialize into. 690 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 691 | * @return {!proto.stocks.StockSymbol} 692 | */ 693 | proto.stocks.StockSymbol.deserializeBinaryFromReader = function(msg, reader) { 694 | while (reader.nextField()) { 695 | if (reader.isEndGroup()) { 696 | break; 697 | } 698 | var field = reader.getFieldNumber(); 699 | switch (field) { 700 | case 1: 701 | var value = /** @type {string} */ (reader.readString()); 702 | msg.setSymbol(value); 703 | break; 704 | default: 705 | reader.skipField(); 706 | break; 707 | } 708 | } 709 | return msg; 710 | }; 711 | 712 | 713 | /** 714 | * Serializes the message to binary data (in protobuf wire format). 715 | * @return {!Uint8Array} 716 | */ 717 | proto.stocks.StockSymbol.prototype.serializeBinary = function() { 718 | var writer = new jspb.BinaryWriter(); 719 | proto.stocks.StockSymbol.serializeBinaryToWriter(this, writer); 720 | return writer.getResultBuffer(); 721 | }; 722 | 723 | 724 | /** 725 | * Serializes the given message to binary data (in protobuf wire 726 | * format), writing to the given BinaryWriter. 727 | * @param {!proto.stocks.StockSymbol} message 728 | * @param {!jspb.BinaryWriter} writer 729 | * @suppress {unusedLocalVariables} f is only used for nested messages 730 | */ 731 | proto.stocks.StockSymbol.serializeBinaryToWriter = function(message, writer) { 732 | var f = undefined; 733 | f = message.getSymbol(); 734 | if (f.length > 0) { 735 | writer.writeString( 736 | 1, 737 | f 738 | ); 739 | } 740 | }; 741 | 742 | 743 | /** 744 | * optional string symbol = 1; 745 | * @return {string} 746 | */ 747 | proto.stocks.StockSymbol.prototype.getSymbol = function() { 748 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); 749 | }; 750 | 751 | 752 | /** 753 | * @param {string} value 754 | * @return {!proto.stocks.StockSymbol} returns this 755 | */ 756 | proto.stocks.StockSymbol.prototype.setSymbol = function(value) { 757 | return jspb.Message.setProto3StringField(this, 1, value); 758 | }; 759 | 760 | 761 | 762 | 763 | 764 | if (jspb.Message.GENERATE_TO_OBJECT) { 765 | /** 766 | * Creates an object representation of this proto. 767 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 768 | * Optional fields that are not set will be set to undefined. 769 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 770 | * For the list of reserved names please see: 771 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 772 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 773 | * JSPB instance for transitional soy proto support: 774 | * http://goto/soy-param-migration 775 | * @return {!Object} 776 | */ 777 | proto.stocks.User.prototype.toObject = function(opt_includeInstance) { 778 | return proto.stocks.User.toObject(opt_includeInstance, this); 779 | }; 780 | 781 | 782 | /** 783 | * Static version of the {@see toObject} method. 784 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 785 | * the JSPB instance for transitional soy proto support: 786 | * http://goto/soy-param-migration 787 | * @param {!proto.stocks.User} msg The msg instance to transform. 788 | * @return {!Object} 789 | * @suppress {unusedLocalVariables} f is only used for nested messages 790 | */ 791 | proto.stocks.User.toObject = function(includeInstance, msg) { 792 | var f, obj = { 793 | username: jspb.Message.getFieldWithDefault(msg, 1, "") 794 | }; 795 | 796 | if (includeInstance) { 797 | obj.$jspbMessageInstance = msg; 798 | } 799 | return obj; 800 | }; 801 | } 802 | 803 | 804 | /** 805 | * Deserializes binary data (in protobuf wire format). 806 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 807 | * @return {!proto.stocks.User} 808 | */ 809 | proto.stocks.User.deserializeBinary = function(bytes) { 810 | var reader = new jspb.BinaryReader(bytes); 811 | var msg = new proto.stocks.User; 812 | return proto.stocks.User.deserializeBinaryFromReader(msg, reader); 813 | }; 814 | 815 | 816 | /** 817 | * Deserializes binary data (in protobuf wire format) from the 818 | * given reader into the given message object. 819 | * @param {!proto.stocks.User} msg The message object to deserialize into. 820 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 821 | * @return {!proto.stocks.User} 822 | */ 823 | proto.stocks.User.deserializeBinaryFromReader = function(msg, reader) { 824 | while (reader.nextField()) { 825 | if (reader.isEndGroup()) { 826 | break; 827 | } 828 | var field = reader.getFieldNumber(); 829 | switch (field) { 830 | case 1: 831 | var value = /** @type {string} */ (reader.readString()); 832 | msg.setUsername(value); 833 | break; 834 | default: 835 | reader.skipField(); 836 | break; 837 | } 838 | } 839 | return msg; 840 | }; 841 | 842 | 843 | /** 844 | * Serializes the message to binary data (in protobuf wire format). 845 | * @return {!Uint8Array} 846 | */ 847 | proto.stocks.User.prototype.serializeBinary = function() { 848 | var writer = new jspb.BinaryWriter(); 849 | proto.stocks.User.serializeBinaryToWriter(this, writer); 850 | return writer.getResultBuffer(); 851 | }; 852 | 853 | 854 | /** 855 | * Serializes the given message to binary data (in protobuf wire 856 | * format), writing to the given BinaryWriter. 857 | * @param {!proto.stocks.User} message 858 | * @param {!jspb.BinaryWriter} writer 859 | * @suppress {unusedLocalVariables} f is only used for nested messages 860 | */ 861 | proto.stocks.User.serializeBinaryToWriter = function(message, writer) { 862 | var f = undefined; 863 | f = message.getUsername(); 864 | if (f.length > 0) { 865 | writer.writeString( 866 | 1, 867 | f 868 | ); 869 | } 870 | }; 871 | 872 | 873 | /** 874 | * optional string username = 1; 875 | * @return {string} 876 | */ 877 | proto.stocks.User.prototype.getUsername = function() { 878 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); 879 | }; 880 | 881 | 882 | /** 883 | * @param {string} value 884 | * @return {!proto.stocks.User} returns this 885 | */ 886 | proto.stocks.User.prototype.setUsername = function(value) { 887 | return jspb.Message.setProto3StringField(this, 1, value); 888 | }; 889 | 890 | 891 | 892 | /** 893 | * List of repeated fields within this message type. 894 | * @private {!Array} 895 | * @const 896 | */ 897 | proto.stocks.UserStock.repeatedFields_ = [2]; 898 | 899 | 900 | 901 | if (jspb.Message.GENERATE_TO_OBJECT) { 902 | /** 903 | * Creates an object representation of this proto. 904 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 905 | * Optional fields that are not set will be set to undefined. 906 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 907 | * For the list of reserved names please see: 908 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 909 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 910 | * JSPB instance for transitional soy proto support: 911 | * http://goto/soy-param-migration 912 | * @return {!Object} 913 | */ 914 | proto.stocks.UserStock.prototype.toObject = function(opt_includeInstance) { 915 | return proto.stocks.UserStock.toObject(opt_includeInstance, this); 916 | }; 917 | 918 | 919 | /** 920 | * Static version of the {@see toObject} method. 921 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 922 | * the JSPB instance for transitional soy proto support: 923 | * http://goto/soy-param-migration 924 | * @param {!proto.stocks.UserStock} msg The msg instance to transform. 925 | * @return {!Object} 926 | * @suppress {unusedLocalVariables} f is only used for nested messages 927 | */ 928 | proto.stocks.UserStock.toObject = function(includeInstance, msg) { 929 | var f, obj = { 930 | username: jspb.Message.getFieldWithDefault(msg, 1, ""), 931 | symbolList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f 932 | }; 933 | 934 | if (includeInstance) { 935 | obj.$jspbMessageInstance = msg; 936 | } 937 | return obj; 938 | }; 939 | } 940 | 941 | 942 | /** 943 | * Deserializes binary data (in protobuf wire format). 944 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 945 | * @return {!proto.stocks.UserStock} 946 | */ 947 | proto.stocks.UserStock.deserializeBinary = function(bytes) { 948 | var reader = new jspb.BinaryReader(bytes); 949 | var msg = new proto.stocks.UserStock; 950 | return proto.stocks.UserStock.deserializeBinaryFromReader(msg, reader); 951 | }; 952 | 953 | 954 | /** 955 | * Deserializes binary data (in protobuf wire format) from the 956 | * given reader into the given message object. 957 | * @param {!proto.stocks.UserStock} msg The message object to deserialize into. 958 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 959 | * @return {!proto.stocks.UserStock} 960 | */ 961 | proto.stocks.UserStock.deserializeBinaryFromReader = function(msg, reader) { 962 | while (reader.nextField()) { 963 | if (reader.isEndGroup()) { 964 | break; 965 | } 966 | var field = reader.getFieldNumber(); 967 | switch (field) { 968 | case 1: 969 | var value = /** @type {string} */ (reader.readString()); 970 | msg.setUsername(value); 971 | break; 972 | case 2: 973 | var value = /** @type {string} */ (reader.readString()); 974 | msg.addSymbol(value); 975 | break; 976 | default: 977 | reader.skipField(); 978 | break; 979 | } 980 | } 981 | return msg; 982 | }; 983 | 984 | 985 | /** 986 | * Serializes the message to binary data (in protobuf wire format). 987 | * @return {!Uint8Array} 988 | */ 989 | proto.stocks.UserStock.prototype.serializeBinary = function() { 990 | var writer = new jspb.BinaryWriter(); 991 | proto.stocks.UserStock.serializeBinaryToWriter(this, writer); 992 | return writer.getResultBuffer(); 993 | }; 994 | 995 | 996 | /** 997 | * Serializes the given message to binary data (in protobuf wire 998 | * format), writing to the given BinaryWriter. 999 | * @param {!proto.stocks.UserStock} message 1000 | * @param {!jspb.BinaryWriter} writer 1001 | * @suppress {unusedLocalVariables} f is only used for nested messages 1002 | */ 1003 | proto.stocks.UserStock.serializeBinaryToWriter = function(message, writer) { 1004 | var f = undefined; 1005 | f = message.getUsername(); 1006 | if (f.length > 0) { 1007 | writer.writeString( 1008 | 1, 1009 | f 1010 | ); 1011 | } 1012 | f = message.getSymbolList(); 1013 | if (f.length > 0) { 1014 | writer.writeRepeatedString( 1015 | 2, 1016 | f 1017 | ); 1018 | } 1019 | }; 1020 | 1021 | 1022 | /** 1023 | * optional string username = 1; 1024 | * @return {string} 1025 | */ 1026 | proto.stocks.UserStock.prototype.getUsername = function() { 1027 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); 1028 | }; 1029 | 1030 | 1031 | /** 1032 | * @param {string} value 1033 | * @return {!proto.stocks.UserStock} returns this 1034 | */ 1035 | proto.stocks.UserStock.prototype.setUsername = function(value) { 1036 | return jspb.Message.setProto3StringField(this, 1, value); 1037 | }; 1038 | 1039 | 1040 | /** 1041 | * repeated string symbol = 2; 1042 | * @return {!Array} 1043 | */ 1044 | proto.stocks.UserStock.prototype.getSymbolList = function() { 1045 | return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 2)); 1046 | }; 1047 | 1048 | 1049 | /** 1050 | * @param {!Array} value 1051 | * @return {!proto.stocks.UserStock} returns this 1052 | */ 1053 | proto.stocks.UserStock.prototype.setSymbolList = function(value) { 1054 | return jspb.Message.setField(this, 2, value || []); 1055 | }; 1056 | 1057 | 1058 | /** 1059 | * @param {string} value 1060 | * @param {number=} opt_index 1061 | * @return {!proto.stocks.UserStock} returns this 1062 | */ 1063 | proto.stocks.UserStock.prototype.addSymbol = function(value, opt_index) { 1064 | return jspb.Message.addToRepeatedField(this, 2, value, opt_index); 1065 | }; 1066 | 1067 | 1068 | /** 1069 | * Clears the list making it empty but non-null. 1070 | * @return {!proto.stocks.UserStock} returns this 1071 | */ 1072 | proto.stocks.UserStock.prototype.clearSymbolList = function() { 1073 | return this.setSymbolList([]); 1074 | }; 1075 | 1076 | 1077 | goog.object.extend(exports, proto.stocks); 1078 | -------------------------------------------------------------------------------- /example/frontend_react/src/App.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React, { useEffect, useState } from 'react'; 4 | import { grpcExpressClient } from '@grpcexpress/grpcexpress'; 5 | import ButtonGroupComponent from './components/ButtonGroupComponent'; 6 | import { StocksServiceClient } from '../protos/StocksServiceClientPb'; 7 | import { Container, Stack } from '@mui/material'; 8 | import Responses from './components/Responses'; 9 | import { User } from '../protos/stocks_pb'; 10 | import { useGrpcExpress } from '@grpcexpress/usegrpcexpress'; 11 | // import { useGrpcExpress } from "./useGrpcExpress"; 12 | 13 | type Response = { 14 | timeSpan: number; 15 | symbol: string; 16 | name: string; 17 | price: number; 18 | }; 19 | 20 | export default function App() { 21 | const [responses, setResponse] = useState([]); 22 | 23 | // initialize a Grpc client by passing in the original client into our custom grpcExpressClient function 24 | const Client = grpcExpressClient(StocksServiceClient); 25 | const client = new Client('http://localhost:8080'); 26 | 27 | const testUser = new User(); 28 | testUser.setUsername('Murat'); 29 | 30 | const { isLoading, isError, data, error } = useGrpcExpress( 31 | client.getStocks, 32 | testUser 33 | ); 34 | 35 | async function getStockInfo( 36 | e: React.MouseEvent 37 | ) { 38 | const element = e.target as HTMLButtonElement; 39 | const username = element.value; 40 | const user = new User(); 41 | user.setUsername(username); 42 | const before = Date.now(); 43 | const stockList = await client.getStocks(user, {}); 44 | const timeSpan = Date.now() - before; 45 | const stocksObject = stockList.toObject(); 46 | const stocksListWithTime = stocksObject.stocksList.map(e => ({ 47 | timeSpan, 48 | symbol: e.symbol, 49 | name: e.name, 50 | price: e.price, 51 | })); 52 | setResponse(prev => [...prev, ...stocksListWithTime]); 53 | } 54 | 55 | useEffect(() => {}, []); 56 | 57 | return ( 58 |
59 | {isLoading.toString()} 60 | {isError.toString()} 61 | {data?.toString()} 62 | {error?.toString()} 63 | 64 | 65 | 66 | 67 | 68 | 69 |
70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /example/frontend_react/src/components/ButtonGroupComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from '@mui/material/Button'; 3 | import ButtonGroup from '@mui/material/ButtonGroup'; 4 | import Stack from '@mui/material/Stack'; 5 | 6 | type ButtonGroupComponentProps = { 7 | handleClick: (e: any) => void; 8 | }; 9 | 10 | export default function ButtonGroupComponent({ 11 | handleClick, 12 | }: ButtonGroupComponentProps) { 13 | return ( 14 | 21 | 26 | 29 | 32 | 35 | 38 | 39 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /example/frontend_react/src/components/Layout.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | import Navbar from './Navbar'; 3 | 4 | type LayoutProps = { 5 | children: ReactNode; 6 | }; 7 | 8 | export default function Layout({ children }: LayoutProps) { 9 | return ( 10 | <> 11 | 12 | {children} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /example/frontend_react/src/components/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import AppBar from '@mui/material/AppBar'; 3 | import Box from '@mui/material/Box'; 4 | import Toolbar from '@mui/material/Toolbar'; 5 | import Typography from '@mui/material/Typography'; 6 | import IconButton from '@mui/material/IconButton'; 7 | 8 | export default function Navbar() { 9 | return ( 10 | 11 | 12 | 13 | 20 | 26 | GRPC Express 27 | 28 | 29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /example/frontend_react/src/components/Responses.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Stack, Typography } from '@mui/material'; 3 | import { DataGrid, GridRowsProp, GridColDef } from '@mui/x-data-grid'; 4 | 5 | import { Stock } from '../../protos/stocks_pb'; 6 | 7 | // type Responses 8 | type Response = { 9 | timeSpan: number; 10 | symbol: string; 11 | name: string; 12 | price: number; 13 | }; 14 | 15 | type ResponsesProps = { 16 | responses: Response[]; 17 | }; 18 | 19 | export default function Responses({ responses }: ResponsesProps) { 20 | const rows: GridRowsProp = responses.map((stock, index) => ({ 21 | id: index, 22 | timeSpan: stock.timeSpan, 23 | symbol: stock.symbol, 24 | name: stock.name, 25 | price: stock.price.toFixed(2), 26 | })); 27 | 28 | const columns: GridColDef[] = [ 29 | { field: 'timeSpan', headerName: 'Duration in MS', width: 150 }, 30 | { field: 'symbol', headerName: 'Stock Symbol', width: 150 }, 31 | { field: 'name', headerName: 'Stock Name', width: 150 }, 32 | { field: 'price', headerName: 'Stock Price', width: 150 }, 33 | ]; 34 | 35 | return ; 36 | } 37 | -------------------------------------------------------------------------------- /example/frontend_react/src/components/Store.tsx: -------------------------------------------------------------------------------- 1 | import { List, ListItem, ListItemText } from '@mui/material'; 2 | import React from 'react'; 3 | 4 | type StoreProps = { 5 | store: { store: any[] }; 6 | }; 7 | 8 | export default function Store({ store }) { 9 | const storeArray = store.store as Record; 10 | 11 | if (!storeArray) return null; 12 | 13 | const array: any[] = []; 14 | 15 | storeArray.forEach((v, k) => 16 | array.push( 17 | 18 | 19 | 20 | 21 | 22 | ) 23 | ); 24 | 25 | return <>{array}; 26 | } 27 | -------------------------------------------------------------------------------- /example/frontend_react/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | gRPC React Client 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /example/frontend_react/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import App from './App'; 4 | import CssBaseline from '@mui/material/CssBaseline'; 5 | import Layout from './components/Layout'; 6 | 7 | const container = document.getElementById('root') as HTMLDivElement; 8 | const root = createRoot(container); 9 | 10 | root.render( 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /example/frontend_react/src/useGrpcExpress.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export function useGrpcExpress(client, message) { 4 | const [isLoading, setIsLoading] = useState(true); 5 | const [isError, setIsError] = useState(false); 6 | const [data, setData] = useState(null); 7 | const [error, setError] = useState(null); 8 | 9 | useEffect(() => { 10 | client(message) 11 | .then(res => { 12 | setData(res), setIsLoading(false); 13 | }) 14 | .catch(err => { 15 | setError(err), setIsError(true); 16 | }); 17 | }, [client, message]); 18 | 19 | return { 20 | isLoading, 21 | isError, 22 | data, 23 | error, 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /example/frontend_react/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | 4 | module.exports = { 5 | entry: ['./src/index.tsx'], 6 | output: { 7 | filename: 'bundle.js', 8 | path: path.resolve(__dirname, 'dist'), 9 | publicPath: '/', 10 | }, 11 | mode: 'development', 12 | resolve: { 13 | extensions: ['.tsx', '.ts', '.js', '.jsx'], 14 | }, 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.[jt]sx?$/, 19 | loader: 'esbuild-loader', 20 | options: { 21 | target: 'es2015', 22 | }, 23 | }, 24 | ], 25 | }, 26 | plugins: [ 27 | new HtmlWebpackPlugin({ 28 | template: './src/index.html', 29 | }), 30 | ], 31 | devServer: { 32 | port: 8081, 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /example/server/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es2021: true, 6 | }, 7 | extends: [ 8 | 'eslint:recommended', 9 | 'plugin:@typescript-eslint/recommended', 10 | 'prettier', 11 | ], 12 | overrides: [ 13 | { 14 | env: { 15 | node: true, 16 | }, 17 | files: ['.eslintrc.{js,cjs}'], 18 | parserOptions: { 19 | sourceType: 'script', 20 | }, 21 | }, 22 | ], 23 | parser: '@typescript-eslint/parser', 24 | parserOptions: { 25 | ecmaVersion: 'latest', 26 | }, 27 | plugins: ['@typescript-eslint'], 28 | rules: {}, 29 | }; 30 | -------------------------------------------------------------------------------- /example/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node dist/server.ts", 8 | "dev": "ts-node src/server_static.ts", 9 | "dev-client": "ts-node src/client_static.ts", 10 | "build": "tsc --build", 11 | "tsproto": "protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=esModuleInterop=true --ts_proto_out=. ./protos/todos.proto", 12 | "protoc": "npx protoc --ts_out protos/. --proto_path protos protos/stocks.proto" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "@grpc/grpc-js": "^1.9.0", 18 | "@grpc/proto-loader": "^0.7.8", 19 | "@protobuf-ts/grpc-transport": "^2.9.1", 20 | "@protobuf-ts/plugin": "^2.9.1" 21 | }, 22 | "devDependencies": { 23 | "@types/node": "^20.5.1", 24 | "@typescript-eslint/eslint-plugin": "^6.4.0", 25 | "@typescript-eslint/parser": "^6.4.0", 26 | "eslint": "^8.47.0", 27 | "eslint-config-prettier": "^9.0.0", 28 | "jest": "^29.6.2", 29 | "nodemon": "^3.0.1", 30 | "prettier": "^3.0.2", 31 | "stylelint-config-prettier": "^9.0.5", 32 | "ts-node": "^10.9.1", 33 | "typescript": "^5.1.6" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /example/server/protos/stocks 2.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package stocks; 4 | 5 | import "protobuf-ts.proto"; 6 | 7 | service StocksService { 8 | option (ts.server) = GRPC1_SERVER; 9 | option (ts.client) = GRPC1_CLIENT; 10 | rpc GetStocks (User) returns (StocksList) {} 11 | rpc AddStock (UserStock) returns (StocksList) {} 12 | rpc DeleteStock (UserStock) returns (StocksList) {} 13 | } 14 | 15 | message Empty {} 16 | 17 | message StocksList { 18 | repeated Stock stocks = 1; 19 | } 20 | 21 | message Stock { 22 | string symbol = 1; 23 | string name = 2; 24 | float price = 3; 25 | string time = 4; 26 | } 27 | 28 | message StockSymbol { 29 | string symbol = 1; 30 | } 31 | 32 | message User { 33 | string username = 1; 34 | } 35 | 36 | message UserStock { 37 | string username = 1; 38 | repeated string symbol = 2; 39 | } -------------------------------------------------------------------------------- /example/server/protos/stocks 2.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.9.1 2 | // @generated from protobuf file "stocks.proto" (package "stocks", syntax proto3) 3 | // tslint:disable 4 | import { ServiceType } from "@protobuf-ts/runtime-rpc"; 5 | import { WireType } from "@protobuf-ts/runtime"; 6 | import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; 7 | import type { IBinaryWriter } from "@protobuf-ts/runtime"; 8 | import { UnknownFieldHandler } from "@protobuf-ts/runtime"; 9 | import type { BinaryReadOptions } from "@protobuf-ts/runtime"; 10 | import type { IBinaryReader } from "@protobuf-ts/runtime"; 11 | import type { PartialMessage } from "@protobuf-ts/runtime"; 12 | import { reflectionMergePartial } from "@protobuf-ts/runtime"; 13 | import { MESSAGE_TYPE } from "@protobuf-ts/runtime"; 14 | import { MessageType } from "@protobuf-ts/runtime"; 15 | /** 16 | * @generated from protobuf message stocks.Empty 17 | */ 18 | export interface Empty { 19 | } 20 | /** 21 | * @generated from protobuf message stocks.StocksList 22 | */ 23 | export interface StocksList { 24 | /** 25 | * @generated from protobuf field: repeated stocks.Stock stocks = 1; 26 | */ 27 | stocks: Stock[]; 28 | } 29 | /** 30 | * @generated from protobuf message stocks.Stock 31 | */ 32 | export interface Stock { 33 | /** 34 | * @generated from protobuf field: string symbol = 1; 35 | */ 36 | symbol: string; 37 | /** 38 | * @generated from protobuf field: string name = 2; 39 | */ 40 | name: string; 41 | /** 42 | * @generated from protobuf field: float price = 3; 43 | */ 44 | price: number; 45 | /** 46 | * @generated from protobuf field: string time = 4; 47 | */ 48 | time: string; 49 | } 50 | /** 51 | * @generated from protobuf message stocks.StockSymbol 52 | */ 53 | export interface StockSymbol { 54 | /** 55 | * @generated from protobuf field: string symbol = 1; 56 | */ 57 | symbol: string; 58 | } 59 | /** 60 | * @generated from protobuf message stocks.User 61 | */ 62 | export interface User { 63 | /** 64 | * @generated from protobuf field: string username = 1; 65 | */ 66 | username: string; 67 | } 68 | /** 69 | * @generated from protobuf message stocks.UserStock 70 | */ 71 | export interface UserStock { 72 | /** 73 | * @generated from protobuf field: string username = 1; 74 | */ 75 | username: string; 76 | /** 77 | * @generated from protobuf field: repeated string symbol = 2; 78 | */ 79 | symbol: string[]; 80 | } 81 | // @generated message type with reflection information, may provide speed optimized methods 82 | class Empty$Type extends MessageType { 83 | constructor() { 84 | super("stocks.Empty", []); 85 | } 86 | create(value?: PartialMessage): Empty { 87 | const message = {}; 88 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 89 | if (value !== undefined) 90 | reflectionMergePartial(this, message, value); 91 | return message; 92 | } 93 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Empty): Empty { 94 | return target ?? this.create(); 95 | } 96 | internalBinaryWrite(message: Empty, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 97 | let u = options.writeUnknownFields; 98 | if (u !== false) 99 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 100 | return writer; 101 | } 102 | } 103 | /** 104 | * @generated MessageType for protobuf message stocks.Empty 105 | */ 106 | export const Empty = new Empty$Type(); 107 | // @generated message type with reflection information, may provide speed optimized methods 108 | class StocksList$Type extends MessageType { 109 | constructor() { 110 | super("stocks.StocksList", [ 111 | { no: 1, name: "stocks", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => Stock } 112 | ]); 113 | } 114 | create(value?: PartialMessage): StocksList { 115 | const message = { stocks: [] }; 116 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 117 | if (value !== undefined) 118 | reflectionMergePartial(this, message, value); 119 | return message; 120 | } 121 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: StocksList): StocksList { 122 | let message = target ?? this.create(), end = reader.pos + length; 123 | while (reader.pos < end) { 124 | let [fieldNo, wireType] = reader.tag(); 125 | switch (fieldNo) { 126 | case /* repeated stocks.Stock stocks */ 1: 127 | message.stocks.push(Stock.internalBinaryRead(reader, reader.uint32(), options)); 128 | break; 129 | default: 130 | let u = options.readUnknownField; 131 | if (u === "throw") 132 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 133 | let d = reader.skip(wireType); 134 | if (u !== false) 135 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 136 | } 137 | } 138 | return message; 139 | } 140 | internalBinaryWrite(message: StocksList, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 141 | /* repeated stocks.Stock stocks = 1; */ 142 | for (let i = 0; i < message.stocks.length; i++) 143 | Stock.internalBinaryWrite(message.stocks[i], writer.tag(1, WireType.LengthDelimited).fork(), options).join(); 144 | let u = options.writeUnknownFields; 145 | if (u !== false) 146 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 147 | return writer; 148 | } 149 | } 150 | /** 151 | * @generated MessageType for protobuf message stocks.StocksList 152 | */ 153 | export const StocksList = new StocksList$Type(); 154 | // @generated message type with reflection information, may provide speed optimized methods 155 | class Stock$Type extends MessageType { 156 | constructor() { 157 | super("stocks.Stock", [ 158 | { no: 1, name: "symbol", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, 159 | { no: 2, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, 160 | { no: 3, name: "price", kind: "scalar", T: 2 /*ScalarType.FLOAT*/ }, 161 | { no: 4, name: "time", kind: "scalar", T: 9 /*ScalarType.STRING*/ } 162 | ]); 163 | } 164 | create(value?: PartialMessage): Stock { 165 | const message = { symbol: "", name: "", price: 0, time: "" }; 166 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 167 | if (value !== undefined) 168 | reflectionMergePartial(this, message, value); 169 | return message; 170 | } 171 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Stock): Stock { 172 | let message = target ?? this.create(), end = reader.pos + length; 173 | while (reader.pos < end) { 174 | let [fieldNo, wireType] = reader.tag(); 175 | switch (fieldNo) { 176 | case /* string symbol */ 1: 177 | message.symbol = reader.string(); 178 | break; 179 | case /* string name */ 2: 180 | message.name = reader.string(); 181 | break; 182 | case /* float price */ 3: 183 | message.price = reader.float(); 184 | break; 185 | case /* string time */ 4: 186 | message.time = reader.string(); 187 | break; 188 | default: 189 | let u = options.readUnknownField; 190 | if (u === "throw") 191 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 192 | let d = reader.skip(wireType); 193 | if (u !== false) 194 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 195 | } 196 | } 197 | return message; 198 | } 199 | internalBinaryWrite(message: Stock, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 200 | /* string symbol = 1; */ 201 | if (message.symbol !== "") 202 | writer.tag(1, WireType.LengthDelimited).string(message.symbol); 203 | /* string name = 2; */ 204 | if (message.name !== "") 205 | writer.tag(2, WireType.LengthDelimited).string(message.name); 206 | /* float price = 3; */ 207 | if (message.price !== 0) 208 | writer.tag(3, WireType.Bit32).float(message.price); 209 | /* string time = 4; */ 210 | if (message.time !== "") 211 | writer.tag(4, WireType.LengthDelimited).string(message.time); 212 | let u = options.writeUnknownFields; 213 | if (u !== false) 214 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 215 | return writer; 216 | } 217 | } 218 | /** 219 | * @generated MessageType for protobuf message stocks.Stock 220 | */ 221 | export const Stock = new Stock$Type(); 222 | // @generated message type with reflection information, may provide speed optimized methods 223 | class StockSymbol$Type extends MessageType { 224 | constructor() { 225 | super("stocks.StockSymbol", [ 226 | { no: 1, name: "symbol", kind: "scalar", T: 9 /*ScalarType.STRING*/ } 227 | ]); 228 | } 229 | create(value?: PartialMessage): StockSymbol { 230 | const message = { symbol: "" }; 231 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 232 | if (value !== undefined) 233 | reflectionMergePartial(this, message, value); 234 | return message; 235 | } 236 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: StockSymbol): StockSymbol { 237 | let message = target ?? this.create(), end = reader.pos + length; 238 | while (reader.pos < end) { 239 | let [fieldNo, wireType] = reader.tag(); 240 | switch (fieldNo) { 241 | case /* string symbol */ 1: 242 | message.symbol = reader.string(); 243 | break; 244 | default: 245 | let u = options.readUnknownField; 246 | if (u === "throw") 247 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 248 | let d = reader.skip(wireType); 249 | if (u !== false) 250 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 251 | } 252 | } 253 | return message; 254 | } 255 | internalBinaryWrite(message: StockSymbol, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 256 | /* string symbol = 1; */ 257 | if (message.symbol !== "") 258 | writer.tag(1, WireType.LengthDelimited).string(message.symbol); 259 | let u = options.writeUnknownFields; 260 | if (u !== false) 261 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 262 | return writer; 263 | } 264 | } 265 | /** 266 | * @generated MessageType for protobuf message stocks.StockSymbol 267 | */ 268 | export const StockSymbol = new StockSymbol$Type(); 269 | // @generated message type with reflection information, may provide speed optimized methods 270 | class User$Type extends MessageType { 271 | constructor() { 272 | super("stocks.User", [ 273 | { no: 1, name: "username", kind: "scalar", T: 9 /*ScalarType.STRING*/ } 274 | ]); 275 | } 276 | create(value?: PartialMessage): User { 277 | const message = { username: "" }; 278 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 279 | if (value !== undefined) 280 | reflectionMergePartial(this, message, value); 281 | return message; 282 | } 283 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: User): User { 284 | let message = target ?? this.create(), end = reader.pos + length; 285 | while (reader.pos < end) { 286 | let [fieldNo, wireType] = reader.tag(); 287 | switch (fieldNo) { 288 | case /* string username */ 1: 289 | message.username = reader.string(); 290 | break; 291 | default: 292 | let u = options.readUnknownField; 293 | if (u === "throw") 294 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 295 | let d = reader.skip(wireType); 296 | if (u !== false) 297 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 298 | } 299 | } 300 | return message; 301 | } 302 | internalBinaryWrite(message: User, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 303 | /* string username = 1; */ 304 | if (message.username !== "") 305 | writer.tag(1, WireType.LengthDelimited).string(message.username); 306 | let u = options.writeUnknownFields; 307 | if (u !== false) 308 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 309 | return writer; 310 | } 311 | } 312 | /** 313 | * @generated MessageType for protobuf message stocks.User 314 | */ 315 | export const User = new User$Type(); 316 | // @generated message type with reflection information, may provide speed optimized methods 317 | class UserStock$Type extends MessageType { 318 | constructor() { 319 | super("stocks.UserStock", [ 320 | { no: 1, name: "username", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, 321 | { no: 2, name: "symbol", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ } 322 | ]); 323 | } 324 | create(value?: PartialMessage): UserStock { 325 | const message = { username: "", symbol: [] }; 326 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 327 | if (value !== undefined) 328 | reflectionMergePartial(this, message, value); 329 | return message; 330 | } 331 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: UserStock): UserStock { 332 | let message = target ?? this.create(), end = reader.pos + length; 333 | while (reader.pos < end) { 334 | let [fieldNo, wireType] = reader.tag(); 335 | switch (fieldNo) { 336 | case /* string username */ 1: 337 | message.username = reader.string(); 338 | break; 339 | case /* repeated string symbol */ 2: 340 | message.symbol.push(reader.string()); 341 | break; 342 | default: 343 | let u = options.readUnknownField; 344 | if (u === "throw") 345 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 346 | let d = reader.skip(wireType); 347 | if (u !== false) 348 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 349 | } 350 | } 351 | return message; 352 | } 353 | internalBinaryWrite(message: UserStock, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 354 | /* string username = 1; */ 355 | if (message.username !== "") 356 | writer.tag(1, WireType.LengthDelimited).string(message.username); 357 | /* repeated string symbol = 2; */ 358 | for (let i = 0; i < message.symbol.length; i++) 359 | writer.tag(2, WireType.LengthDelimited).string(message.symbol[i]); 360 | let u = options.writeUnknownFields; 361 | if (u !== false) 362 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 363 | return writer; 364 | } 365 | } 366 | /** 367 | * @generated MessageType for protobuf message stocks.UserStock 368 | */ 369 | export const UserStock = new UserStock$Type(); 370 | /** 371 | * @generated ServiceType for protobuf service stocks.StocksService 372 | */ 373 | export const StocksService = new ServiceType("stocks.StocksService", [ 374 | { name: "GetStocks", options: {}, I: User, O: StocksList }, 375 | { name: "AddStock", options: {}, I: UserStock, O: StocksList }, 376 | { name: "DeleteStock", options: {}, I: UserStock, O: StocksList } 377 | ], { "ts.server": ["GRPC1_SERVER"] }); 378 | -------------------------------------------------------------------------------- /example/server/protos/stocks.client 2.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.9.1 2 | // @generated from protobuf file "stocks.proto" (package "stocks", syntax proto3) 3 | // tslint:disable 4 | import type { RpcTransport } from "@protobuf-ts/runtime-rpc"; 5 | import type { ServiceInfo } from "@protobuf-ts/runtime-rpc"; 6 | import { StocksService } from "./stocks"; 7 | import type { UserStock } from "./stocks"; 8 | import { stackIntercept } from "@protobuf-ts/runtime-rpc"; 9 | import type { StocksList } from "./stocks"; 10 | import type { User } from "./stocks"; 11 | import type { UnaryCall } from "@protobuf-ts/runtime-rpc"; 12 | import type { RpcOptions } from "@protobuf-ts/runtime-rpc"; 13 | /** 14 | * @generated from protobuf service stocks.StocksService 15 | */ 16 | export interface IStocksServiceClient { 17 | /** 18 | * @generated from protobuf rpc: GetStocks(stocks.User) returns (stocks.StocksList); 19 | */ 20 | getStocks(input: User, options?: RpcOptions): UnaryCall; 21 | /** 22 | * @generated from protobuf rpc: AddStock(stocks.UserStock) returns (stocks.StocksList); 23 | */ 24 | addStock(input: UserStock, options?: RpcOptions): UnaryCall; 25 | /** 26 | * @generated from protobuf rpc: DeleteStock(stocks.UserStock) returns (stocks.StocksList); 27 | */ 28 | deleteStock(input: UserStock, options?: RpcOptions): UnaryCall; 29 | } 30 | /** 31 | * @generated from protobuf service stocks.StocksService 32 | */ 33 | export class StocksServiceClient implements IStocksServiceClient, ServiceInfo { 34 | typeName = StocksService.typeName; 35 | methods = StocksService.methods; 36 | options = StocksService.options; 37 | constructor(private readonly _transport: RpcTransport) { 38 | } 39 | /** 40 | * @generated from protobuf rpc: GetStocks(stocks.User) returns (stocks.StocksList); 41 | */ 42 | getStocks(input: User, options?: RpcOptions): UnaryCall { 43 | const method = this.methods[0], opt = this._transport.mergeOptions(options); 44 | return stackIntercept("unary", this._transport, method, opt, input); 45 | } 46 | /** 47 | * @generated from protobuf rpc: AddStock(stocks.UserStock) returns (stocks.StocksList); 48 | */ 49 | addStock(input: UserStock, options?: RpcOptions): UnaryCall { 50 | const method = this.methods[1], opt = this._transport.mergeOptions(options); 51 | return stackIntercept("unary", this._transport, method, opt, input); 52 | } 53 | /** 54 | * @generated from protobuf rpc: DeleteStock(stocks.UserStock) returns (stocks.StocksList); 55 | */ 56 | deleteStock(input: UserStock, options?: RpcOptions): UnaryCall { 57 | const method = this.methods[2], opt = this._transport.mergeOptions(options); 58 | return stackIntercept("unary", this._transport, method, opt, input); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /example/server/protos/stocks.client.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.9.1 2 | // @generated from protobuf file "stocks.proto" (package "stocks", syntax proto3) 3 | // tslint:disable 4 | import type { RpcTransport } from "@protobuf-ts/runtime-rpc"; 5 | import type { ServiceInfo } from "@protobuf-ts/runtime-rpc"; 6 | import { StocksService } from "./stocks"; 7 | import type { UserStock } from "./stocks"; 8 | import { stackIntercept } from "@protobuf-ts/runtime-rpc"; 9 | import type { StocksList } from "./stocks"; 10 | import type { User } from "./stocks"; 11 | import type { UnaryCall } from "@protobuf-ts/runtime-rpc"; 12 | import type { RpcOptions } from "@protobuf-ts/runtime-rpc"; 13 | /** 14 | * @generated from protobuf service stocks.StocksService 15 | */ 16 | export interface IStocksServiceClient { 17 | /** 18 | * @generated from protobuf rpc: GetStocks(stocks.User) returns (stocks.StocksList); 19 | */ 20 | getStocks(input: User, options?: RpcOptions): UnaryCall; 21 | /** 22 | * @generated from protobuf rpc: AddStock(stocks.UserStock) returns (stocks.StocksList); 23 | */ 24 | addStock(input: UserStock, options?: RpcOptions): UnaryCall; 25 | /** 26 | * @generated from protobuf rpc: DeleteStock(stocks.UserStock) returns (stocks.StocksList); 27 | */ 28 | deleteStock(input: UserStock, options?: RpcOptions): UnaryCall; 29 | } 30 | /** 31 | * @generated from protobuf service stocks.StocksService 32 | */ 33 | export class StocksServiceClient implements IStocksServiceClient, ServiceInfo { 34 | typeName = StocksService.typeName; 35 | methods = StocksService.methods; 36 | options = StocksService.options; 37 | constructor(private readonly _transport: RpcTransport) { 38 | } 39 | /** 40 | * @generated from protobuf rpc: GetStocks(stocks.User) returns (stocks.StocksList); 41 | */ 42 | getStocks(input: User, options?: RpcOptions): UnaryCall { 43 | const method = this.methods[0], opt = this._transport.mergeOptions(options); 44 | return stackIntercept("unary", this._transport, method, opt, input); 45 | } 46 | /** 47 | * @generated from protobuf rpc: AddStock(stocks.UserStock) returns (stocks.StocksList); 48 | */ 49 | addStock(input: UserStock, options?: RpcOptions): UnaryCall { 50 | const method = this.methods[1], opt = this._transport.mergeOptions(options); 51 | return stackIntercept("unary", this._transport, method, opt, input); 52 | } 53 | /** 54 | * @generated from protobuf rpc: DeleteStock(stocks.UserStock) returns (stocks.StocksList); 55 | */ 56 | deleteStock(input: UserStock, options?: RpcOptions): UnaryCall { 57 | const method = this.methods[2], opt = this._transport.mergeOptions(options); 58 | return stackIntercept("unary", this._transport, method, opt, input); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /example/server/protos/stocks.grpc-client 2.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.9.1 2 | // @generated from protobuf file "stocks.proto" (package "stocks", syntax proto3) 3 | // tslint:disable 4 | import { StocksService } from "./stocks"; 5 | import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; 6 | import type { BinaryReadOptions } from "@protobuf-ts/runtime"; 7 | import type { UserStock } from "./stocks"; 8 | import type { StocksList } from "./stocks"; 9 | import type { User } from "./stocks"; 10 | import * as grpc from "@grpc/grpc-js"; 11 | /** 12 | * @generated from protobuf service stocks.StocksService 13 | */ 14 | export interface IStocksServiceClient { 15 | /** 16 | * @generated from protobuf rpc: GetStocks(stocks.User) returns (stocks.StocksList); 17 | */ 18 | getStocks(input: User, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 19 | getStocks(input: User, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 20 | getStocks(input: User, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 21 | getStocks(input: User, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 22 | /** 23 | * @generated from protobuf rpc: AddStock(stocks.UserStock) returns (stocks.StocksList); 24 | */ 25 | addStock(input: UserStock, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 26 | addStock(input: UserStock, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 27 | addStock(input: UserStock, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 28 | addStock(input: UserStock, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 29 | /** 30 | * @generated from protobuf rpc: DeleteStock(stocks.UserStock) returns (stocks.StocksList); 31 | */ 32 | deleteStock(input: UserStock, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 33 | deleteStock(input: UserStock, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 34 | deleteStock(input: UserStock, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 35 | deleteStock(input: UserStock, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 36 | } 37 | /** 38 | * @generated from protobuf service stocks.StocksService 39 | */ 40 | export class StocksServiceClient extends grpc.Client implements IStocksServiceClient { 41 | private readonly _binaryOptions: Partial; 42 | constructor(address: string, credentials: grpc.ChannelCredentials, options: grpc.ClientOptions = {}, binaryOptions: Partial = {}) { 43 | super(address, credentials, options); 44 | this._binaryOptions = binaryOptions; 45 | } 46 | /** 47 | * @generated from protobuf rpc: GetStocks(stocks.User) returns (stocks.StocksList); 48 | */ 49 | getStocks(input: User, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), callback?: ((err: grpc.ServiceError | null, value?: StocksList) => void)): grpc.ClientUnaryCall { 50 | const method = StocksService.methods[0]; 51 | return this.makeUnaryRequest(`/${StocksService.typeName}/${method.name}`, (value: User): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): StocksList => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any)); 52 | } 53 | /** 54 | * @generated from protobuf rpc: AddStock(stocks.UserStock) returns (stocks.StocksList); 55 | */ 56 | addStock(input: UserStock, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), callback?: ((err: grpc.ServiceError | null, value?: StocksList) => void)): grpc.ClientUnaryCall { 57 | const method = StocksService.methods[1]; 58 | return this.makeUnaryRequest(`/${StocksService.typeName}/${method.name}`, (value: UserStock): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): StocksList => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any)); 59 | } 60 | /** 61 | * @generated from protobuf rpc: DeleteStock(stocks.UserStock) returns (stocks.StocksList); 62 | */ 63 | deleteStock(input: UserStock, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), callback?: ((err: grpc.ServiceError | null, value?: StocksList) => void)): grpc.ClientUnaryCall { 64 | const method = StocksService.methods[2]; 65 | return this.makeUnaryRequest(`/${StocksService.typeName}/${method.name}`, (value: UserStock): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): StocksList => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any)); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /example/server/protos/stocks.grpc-client.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.9.1 2 | // @generated from protobuf file "stocks.proto" (package "stocks", syntax proto3) 3 | // tslint:disable 4 | import { StocksService } from "./stocks"; 5 | import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; 6 | import type { BinaryReadOptions } from "@protobuf-ts/runtime"; 7 | import type { UserStock } from "./stocks"; 8 | import type { StocksList } from "./stocks"; 9 | import type { User } from "./stocks"; 10 | import * as grpc from "@grpc/grpc-js"; 11 | /** 12 | * @generated from protobuf service stocks.StocksService 13 | */ 14 | export interface IStocksServiceClient { 15 | /** 16 | * @generated from protobuf rpc: GetStocks(stocks.User) returns (stocks.StocksList); 17 | */ 18 | getStocks(input: User, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 19 | getStocks(input: User, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 20 | getStocks(input: User, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 21 | getStocks(input: User, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 22 | /** 23 | * @generated from protobuf rpc: AddStock(stocks.UserStock) returns (stocks.StocksList); 24 | */ 25 | addStock(input: UserStock, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 26 | addStock(input: UserStock, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 27 | addStock(input: UserStock, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 28 | addStock(input: UserStock, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 29 | /** 30 | * @generated from protobuf rpc: DeleteStock(stocks.UserStock) returns (stocks.StocksList); 31 | */ 32 | deleteStock(input: UserStock, metadata: grpc.Metadata, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 33 | deleteStock(input: UserStock, metadata: grpc.Metadata, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 34 | deleteStock(input: UserStock, options: grpc.CallOptions, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 35 | deleteStock(input: UserStock, callback: (err: grpc.ServiceError | null, value?: StocksList) => void): grpc.ClientUnaryCall; 36 | } 37 | /** 38 | * @generated from protobuf service stocks.StocksService 39 | */ 40 | export class StocksServiceClient extends grpc.Client implements IStocksServiceClient { 41 | private readonly _binaryOptions: Partial; 42 | constructor(address: string, credentials: grpc.ChannelCredentials, options: grpc.ClientOptions = {}, binaryOptions: Partial = {}) { 43 | super(address, credentials, options); 44 | this._binaryOptions = binaryOptions; 45 | } 46 | /** 47 | * @generated from protobuf rpc: GetStocks(stocks.User) returns (stocks.StocksList); 48 | */ 49 | getStocks(input: User, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), callback?: ((err: grpc.ServiceError | null, value?: StocksList) => void)): grpc.ClientUnaryCall { 50 | const method = StocksService.methods[0]; 51 | return this.makeUnaryRequest(`/${StocksService.typeName}/${method.name}`, (value: User): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): StocksList => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any)); 52 | } 53 | /** 54 | * @generated from protobuf rpc: AddStock(stocks.UserStock) returns (stocks.StocksList); 55 | */ 56 | addStock(input: UserStock, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), callback?: ((err: grpc.ServiceError | null, value?: StocksList) => void)): grpc.ClientUnaryCall { 57 | const method = StocksService.methods[1]; 58 | return this.makeUnaryRequest(`/${StocksService.typeName}/${method.name}`, (value: UserStock): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): StocksList => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any)); 59 | } 60 | /** 61 | * @generated from protobuf rpc: DeleteStock(stocks.UserStock) returns (stocks.StocksList); 62 | */ 63 | deleteStock(input: UserStock, metadata: grpc.Metadata | grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), options?: grpc.CallOptions | ((err: grpc.ServiceError | null, value?: StocksList) => void), callback?: ((err: grpc.ServiceError | null, value?: StocksList) => void)): grpc.ClientUnaryCall { 64 | const method = StocksService.methods[2]; 65 | return this.makeUnaryRequest(`/${StocksService.typeName}/${method.name}`, (value: UserStock): Buffer => Buffer.from(method.I.toBinary(value, this._binaryOptions)), (value: Buffer): StocksList => method.O.fromBinary(value, this._binaryOptions), input, (metadata as any), (options as any), (callback as any)); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /example/server/protos/stocks.grpc-server 2.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.9.1 2 | // @generated from protobuf file "stocks.proto" (package "stocks", syntax proto3) 3 | // tslint:disable 4 | import { UserStock } from "./stocks"; 5 | import { StocksList } from "./stocks"; 6 | import { User } from "./stocks"; 7 | import type * as grpc from "@grpc/grpc-js"; 8 | /** 9 | * @generated from protobuf service stocks.StocksService 10 | */ 11 | export interface IStocksService extends grpc.UntypedServiceImplementation { 12 | /** 13 | * @generated from protobuf rpc: GetStocks(stocks.User) returns (stocks.StocksList); 14 | */ 15 | getStocks: grpc.handleUnaryCall; 16 | /** 17 | * @generated from protobuf rpc: AddStock(stocks.UserStock) returns (stocks.StocksList); 18 | */ 19 | addStock: grpc.handleUnaryCall; 20 | /** 21 | * @generated from protobuf rpc: DeleteStock(stocks.UserStock) returns (stocks.StocksList); 22 | */ 23 | deleteStock: grpc.handleUnaryCall; 24 | } 25 | /** 26 | * @grpc/grpc-js definition for the protobuf service stocks.StocksService. 27 | * 28 | * Usage: Implement the interface IStocksService and add to a grpc server. 29 | * 30 | * ```typescript 31 | * const server = new grpc.Server(); 32 | * const service: IStocksService = ... 33 | * server.addService(stocksServiceDefinition, service); 34 | * ``` 35 | */ 36 | export const stocksServiceDefinition: grpc.ServiceDefinition = { 37 | getStocks: { 38 | path: "/stocks.StocksService/GetStocks", 39 | originalName: "GetStocks", 40 | requestStream: false, 41 | responseStream: false, 42 | responseDeserialize: bytes => StocksList.fromBinary(bytes), 43 | requestDeserialize: bytes => User.fromBinary(bytes), 44 | responseSerialize: value => Buffer.from(StocksList.toBinary(value)), 45 | requestSerialize: value => Buffer.from(User.toBinary(value)) 46 | }, 47 | addStock: { 48 | path: "/stocks.StocksService/AddStock", 49 | originalName: "AddStock", 50 | requestStream: false, 51 | responseStream: false, 52 | responseDeserialize: bytes => StocksList.fromBinary(bytes), 53 | requestDeserialize: bytes => UserStock.fromBinary(bytes), 54 | responseSerialize: value => Buffer.from(StocksList.toBinary(value)), 55 | requestSerialize: value => Buffer.from(UserStock.toBinary(value)) 56 | }, 57 | deleteStock: { 58 | path: "/stocks.StocksService/DeleteStock", 59 | originalName: "DeleteStock", 60 | requestStream: false, 61 | responseStream: false, 62 | responseDeserialize: bytes => StocksList.fromBinary(bytes), 63 | requestDeserialize: bytes => UserStock.fromBinary(bytes), 64 | responseSerialize: value => Buffer.from(StocksList.toBinary(value)), 65 | requestSerialize: value => Buffer.from(UserStock.toBinary(value)) 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /example/server/protos/stocks.grpc-server.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.9.1 2 | // @generated from protobuf file "stocks.proto" (package "stocks", syntax proto3) 3 | // tslint:disable 4 | import { UserStock } from "./stocks"; 5 | import { StocksList } from "./stocks"; 6 | import { User } from "./stocks"; 7 | import type * as grpc from "@grpc/grpc-js"; 8 | /** 9 | * @generated from protobuf service stocks.StocksService 10 | */ 11 | export interface IStocksService extends grpc.UntypedServiceImplementation { 12 | /** 13 | * @generated from protobuf rpc: GetStocks(stocks.User) returns (stocks.StocksList); 14 | */ 15 | getStocks: grpc.handleUnaryCall; 16 | /** 17 | * @generated from protobuf rpc: AddStock(stocks.UserStock) returns (stocks.StocksList); 18 | */ 19 | addStock: grpc.handleUnaryCall; 20 | /** 21 | * @generated from protobuf rpc: DeleteStock(stocks.UserStock) returns (stocks.StocksList); 22 | */ 23 | deleteStock: grpc.handleUnaryCall; 24 | } 25 | /** 26 | * @grpc/grpc-js definition for the protobuf service stocks.StocksService. 27 | * 28 | * Usage: Implement the interface IStocksService and add to a grpc server. 29 | * 30 | * ```typescript 31 | * const server = new grpc.Server(); 32 | * const service: IStocksService = ... 33 | * server.addService(stocksServiceDefinition, service); 34 | * ``` 35 | */ 36 | export const stocksServiceDefinition: grpc.ServiceDefinition = { 37 | getStocks: { 38 | path: "/stocks.StocksService/GetStocks", 39 | originalName: "GetStocks", 40 | requestStream: false, 41 | responseStream: false, 42 | responseDeserialize: bytes => StocksList.fromBinary(bytes), 43 | requestDeserialize: bytes => User.fromBinary(bytes), 44 | responseSerialize: value => Buffer.from(StocksList.toBinary(value)), 45 | requestSerialize: value => Buffer.from(User.toBinary(value)) 46 | }, 47 | addStock: { 48 | path: "/stocks.StocksService/AddStock", 49 | originalName: "AddStock", 50 | requestStream: false, 51 | responseStream: false, 52 | responseDeserialize: bytes => StocksList.fromBinary(bytes), 53 | requestDeserialize: bytes => UserStock.fromBinary(bytes), 54 | responseSerialize: value => Buffer.from(StocksList.toBinary(value)), 55 | requestSerialize: value => Buffer.from(UserStock.toBinary(value)) 56 | }, 57 | deleteStock: { 58 | path: "/stocks.StocksService/DeleteStock", 59 | originalName: "DeleteStock", 60 | requestStream: false, 61 | responseStream: false, 62 | responseDeserialize: bytes => StocksList.fromBinary(bytes), 63 | requestDeserialize: bytes => UserStock.fromBinary(bytes), 64 | responseSerialize: value => Buffer.from(StocksList.toBinary(value)), 65 | requestSerialize: value => Buffer.from(UserStock.toBinary(value)) 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /example/server/protos/stocks.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package stocks; 4 | 5 | import "protobuf-ts.proto"; 6 | 7 | service StocksService { 8 | option (ts.server) = GRPC1_SERVER; 9 | option (ts.client) = GRPC1_CLIENT; 10 | rpc GetStocks (User) returns (StocksList) {} 11 | rpc AddStock (UserStock) returns (StocksList) {} 12 | rpc DeleteStock (UserStock) returns (StocksList) {} 13 | } 14 | 15 | message Empty {} 16 | 17 | message StocksList { 18 | repeated Stock stocks = 1; 19 | } 20 | 21 | message Stock { 22 | string symbol = 1; 23 | string name = 2; 24 | float price = 3; 25 | string time = 4; 26 | } 27 | 28 | message StockSymbol { 29 | string symbol = 1; 30 | } 31 | 32 | message User { 33 | string username = 1; 34 | } 35 | 36 | message UserStock { 37 | string username = 1; 38 | repeated string symbol = 2; 39 | } -------------------------------------------------------------------------------- /example/server/protos/stocks.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.9.1 2 | // @generated from protobuf file "stocks.proto" (package "stocks", syntax proto3) 3 | // tslint:disable 4 | import { ServiceType } from "@protobuf-ts/runtime-rpc"; 5 | import { WireType } from "@protobuf-ts/runtime"; 6 | import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; 7 | import type { IBinaryWriter } from "@protobuf-ts/runtime"; 8 | import { UnknownFieldHandler } from "@protobuf-ts/runtime"; 9 | import type { BinaryReadOptions } from "@protobuf-ts/runtime"; 10 | import type { IBinaryReader } from "@protobuf-ts/runtime"; 11 | import type { PartialMessage } from "@protobuf-ts/runtime"; 12 | import { reflectionMergePartial } from "@protobuf-ts/runtime"; 13 | import { MESSAGE_TYPE } from "@protobuf-ts/runtime"; 14 | import { MessageType } from "@protobuf-ts/runtime"; 15 | /** 16 | * @generated from protobuf message stocks.Empty 17 | */ 18 | export interface Empty { 19 | } 20 | /** 21 | * @generated from protobuf message stocks.StocksList 22 | */ 23 | export interface StocksList { 24 | /** 25 | * @generated from protobuf field: repeated stocks.Stock stocks = 1; 26 | */ 27 | stocks: Stock[]; 28 | } 29 | /** 30 | * @generated from protobuf message stocks.Stock 31 | */ 32 | export interface Stock { 33 | /** 34 | * @generated from protobuf field: string symbol = 1; 35 | */ 36 | symbol: string; 37 | /** 38 | * @generated from protobuf field: string name = 2; 39 | */ 40 | name: string; 41 | /** 42 | * @generated from protobuf field: float price = 3; 43 | */ 44 | price: number; 45 | /** 46 | * @generated from protobuf field: string time = 4; 47 | */ 48 | time: string; 49 | } 50 | /** 51 | * @generated from protobuf message stocks.StockSymbol 52 | */ 53 | export interface StockSymbol { 54 | /** 55 | * @generated from protobuf field: string symbol = 1; 56 | */ 57 | symbol: string; 58 | } 59 | /** 60 | * @generated from protobuf message stocks.User 61 | */ 62 | export interface User { 63 | /** 64 | * @generated from protobuf field: string username = 1; 65 | */ 66 | username: string; 67 | } 68 | /** 69 | * @generated from protobuf message stocks.UserStock 70 | */ 71 | export interface UserStock { 72 | /** 73 | * @generated from protobuf field: string username = 1; 74 | */ 75 | username: string; 76 | /** 77 | * @generated from protobuf field: repeated string symbol = 2; 78 | */ 79 | symbol: string[]; 80 | } 81 | // @generated message type with reflection information, may provide speed optimized methods 82 | class Empty$Type extends MessageType { 83 | constructor() { 84 | super("stocks.Empty", []); 85 | } 86 | create(value?: PartialMessage): Empty { 87 | const message = {}; 88 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 89 | if (value !== undefined) 90 | reflectionMergePartial(this, message, value); 91 | return message; 92 | } 93 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Empty): Empty { 94 | return target ?? this.create(); 95 | } 96 | internalBinaryWrite(message: Empty, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 97 | let u = options.writeUnknownFields; 98 | if (u !== false) 99 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 100 | return writer; 101 | } 102 | } 103 | /** 104 | * @generated MessageType for protobuf message stocks.Empty 105 | */ 106 | export const Empty = new Empty$Type(); 107 | // @generated message type with reflection information, may provide speed optimized methods 108 | class StocksList$Type extends MessageType { 109 | constructor() { 110 | super("stocks.StocksList", [ 111 | { no: 1, name: "stocks", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => Stock } 112 | ]); 113 | } 114 | create(value?: PartialMessage): StocksList { 115 | const message = { stocks: [] }; 116 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 117 | if (value !== undefined) 118 | reflectionMergePartial(this, message, value); 119 | return message; 120 | } 121 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: StocksList): StocksList { 122 | let message = target ?? this.create(), end = reader.pos + length; 123 | while (reader.pos < end) { 124 | let [fieldNo, wireType] = reader.tag(); 125 | switch (fieldNo) { 126 | case /* repeated stocks.Stock stocks */ 1: 127 | message.stocks.push(Stock.internalBinaryRead(reader, reader.uint32(), options)); 128 | break; 129 | default: 130 | let u = options.readUnknownField; 131 | if (u === "throw") 132 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 133 | let d = reader.skip(wireType); 134 | if (u !== false) 135 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 136 | } 137 | } 138 | return message; 139 | } 140 | internalBinaryWrite(message: StocksList, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 141 | /* repeated stocks.Stock stocks = 1; */ 142 | for (let i = 0; i < message.stocks.length; i++) 143 | Stock.internalBinaryWrite(message.stocks[i], writer.tag(1, WireType.LengthDelimited).fork(), options).join(); 144 | let u = options.writeUnknownFields; 145 | if (u !== false) 146 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 147 | return writer; 148 | } 149 | } 150 | /** 151 | * @generated MessageType for protobuf message stocks.StocksList 152 | */ 153 | export const StocksList = new StocksList$Type(); 154 | // @generated message type with reflection information, may provide speed optimized methods 155 | class Stock$Type extends MessageType { 156 | constructor() { 157 | super("stocks.Stock", [ 158 | { no: 1, name: "symbol", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, 159 | { no: 2, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, 160 | { no: 3, name: "price", kind: "scalar", T: 2 /*ScalarType.FLOAT*/ }, 161 | { no: 4, name: "time", kind: "scalar", T: 9 /*ScalarType.STRING*/ } 162 | ]); 163 | } 164 | create(value?: PartialMessage): Stock { 165 | const message = { symbol: "", name: "", price: 0, time: "" }; 166 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 167 | if (value !== undefined) 168 | reflectionMergePartial(this, message, value); 169 | return message; 170 | } 171 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: Stock): Stock { 172 | let message = target ?? this.create(), end = reader.pos + length; 173 | while (reader.pos < end) { 174 | let [fieldNo, wireType] = reader.tag(); 175 | switch (fieldNo) { 176 | case /* string symbol */ 1: 177 | message.symbol = reader.string(); 178 | break; 179 | case /* string name */ 2: 180 | message.name = reader.string(); 181 | break; 182 | case /* float price */ 3: 183 | message.price = reader.float(); 184 | break; 185 | case /* string time */ 4: 186 | message.time = reader.string(); 187 | break; 188 | default: 189 | let u = options.readUnknownField; 190 | if (u === "throw") 191 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 192 | let d = reader.skip(wireType); 193 | if (u !== false) 194 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 195 | } 196 | } 197 | return message; 198 | } 199 | internalBinaryWrite(message: Stock, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 200 | /* string symbol = 1; */ 201 | if (message.symbol !== "") 202 | writer.tag(1, WireType.LengthDelimited).string(message.symbol); 203 | /* string name = 2; */ 204 | if (message.name !== "") 205 | writer.tag(2, WireType.LengthDelimited).string(message.name); 206 | /* float price = 3; */ 207 | if (message.price !== 0) 208 | writer.tag(3, WireType.Bit32).float(message.price); 209 | /* string time = 4; */ 210 | if (message.time !== "") 211 | writer.tag(4, WireType.LengthDelimited).string(message.time); 212 | let u = options.writeUnknownFields; 213 | if (u !== false) 214 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 215 | return writer; 216 | } 217 | } 218 | /** 219 | * @generated MessageType for protobuf message stocks.Stock 220 | */ 221 | export const Stock = new Stock$Type(); 222 | // @generated message type with reflection information, may provide speed optimized methods 223 | class StockSymbol$Type extends MessageType { 224 | constructor() { 225 | super("stocks.StockSymbol", [ 226 | { no: 1, name: "symbol", kind: "scalar", T: 9 /*ScalarType.STRING*/ } 227 | ]); 228 | } 229 | create(value?: PartialMessage): StockSymbol { 230 | const message = { symbol: "" }; 231 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 232 | if (value !== undefined) 233 | reflectionMergePartial(this, message, value); 234 | return message; 235 | } 236 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: StockSymbol): StockSymbol { 237 | let message = target ?? this.create(), end = reader.pos + length; 238 | while (reader.pos < end) { 239 | let [fieldNo, wireType] = reader.tag(); 240 | switch (fieldNo) { 241 | case /* string symbol */ 1: 242 | message.symbol = reader.string(); 243 | break; 244 | default: 245 | let u = options.readUnknownField; 246 | if (u === "throw") 247 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 248 | let d = reader.skip(wireType); 249 | if (u !== false) 250 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 251 | } 252 | } 253 | return message; 254 | } 255 | internalBinaryWrite(message: StockSymbol, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 256 | /* string symbol = 1; */ 257 | if (message.symbol !== "") 258 | writer.tag(1, WireType.LengthDelimited).string(message.symbol); 259 | let u = options.writeUnknownFields; 260 | if (u !== false) 261 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 262 | return writer; 263 | } 264 | } 265 | /** 266 | * @generated MessageType for protobuf message stocks.StockSymbol 267 | */ 268 | export const StockSymbol = new StockSymbol$Type(); 269 | // @generated message type with reflection information, may provide speed optimized methods 270 | class User$Type extends MessageType { 271 | constructor() { 272 | super("stocks.User", [ 273 | { no: 1, name: "username", kind: "scalar", T: 9 /*ScalarType.STRING*/ } 274 | ]); 275 | } 276 | create(value?: PartialMessage): User { 277 | const message = { username: "" }; 278 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 279 | if (value !== undefined) 280 | reflectionMergePartial(this, message, value); 281 | return message; 282 | } 283 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: User): User { 284 | let message = target ?? this.create(), end = reader.pos + length; 285 | while (reader.pos < end) { 286 | let [fieldNo, wireType] = reader.tag(); 287 | switch (fieldNo) { 288 | case /* string username */ 1: 289 | message.username = reader.string(); 290 | break; 291 | default: 292 | let u = options.readUnknownField; 293 | if (u === "throw") 294 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 295 | let d = reader.skip(wireType); 296 | if (u !== false) 297 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 298 | } 299 | } 300 | return message; 301 | } 302 | internalBinaryWrite(message: User, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 303 | /* string username = 1; */ 304 | if (message.username !== "") 305 | writer.tag(1, WireType.LengthDelimited).string(message.username); 306 | let u = options.writeUnknownFields; 307 | if (u !== false) 308 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 309 | return writer; 310 | } 311 | } 312 | /** 313 | * @generated MessageType for protobuf message stocks.User 314 | */ 315 | export const User = new User$Type(); 316 | // @generated message type with reflection information, may provide speed optimized methods 317 | class UserStock$Type extends MessageType { 318 | constructor() { 319 | super("stocks.UserStock", [ 320 | { no: 1, name: "username", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, 321 | { no: 2, name: "symbol", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ } 322 | ]); 323 | } 324 | create(value?: PartialMessage): UserStock { 325 | const message = { username: "", symbol: [] }; 326 | globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this }); 327 | if (value !== undefined) 328 | reflectionMergePartial(this, message, value); 329 | return message; 330 | } 331 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: UserStock): UserStock { 332 | let message = target ?? this.create(), end = reader.pos + length; 333 | while (reader.pos < end) { 334 | let [fieldNo, wireType] = reader.tag(); 335 | switch (fieldNo) { 336 | case /* string username */ 1: 337 | message.username = reader.string(); 338 | break; 339 | case /* repeated string symbol */ 2: 340 | message.symbol.push(reader.string()); 341 | break; 342 | default: 343 | let u = options.readUnknownField; 344 | if (u === "throw") 345 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 346 | let d = reader.skip(wireType); 347 | if (u !== false) 348 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 349 | } 350 | } 351 | return message; 352 | } 353 | internalBinaryWrite(message: UserStock, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 354 | /* string username = 1; */ 355 | if (message.username !== "") 356 | writer.tag(1, WireType.LengthDelimited).string(message.username); 357 | /* repeated string symbol = 2; */ 358 | for (let i = 0; i < message.symbol.length; i++) 359 | writer.tag(2, WireType.LengthDelimited).string(message.symbol[i]); 360 | let u = options.writeUnknownFields; 361 | if (u !== false) 362 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 363 | return writer; 364 | } 365 | } 366 | /** 367 | * @generated MessageType for protobuf message stocks.UserStock 368 | */ 369 | export const UserStock = new UserStock$Type(); 370 | /** 371 | * @generated ServiceType for protobuf service stocks.StocksService 372 | */ 373 | export const StocksService = new ServiceType("stocks.StocksService", [ 374 | { name: "GetStocks", options: {}, I: User, O: StocksList }, 375 | { name: "AddStock", options: {}, I: UserStock, O: StocksList }, 376 | { name: "DeleteStock", options: {}, I: UserStock, O: StocksList } 377 | ], { "ts.server": ["GRPC1_SERVER"] }); 378 | -------------------------------------------------------------------------------- /example/server/protos/todos 2.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package todos; 4 | 5 | service TodoApp { 6 | rpc GetTodos (Empty) returns (TodoList) {} 7 | rpc AddTodo (Todo) returns (Empty) {} 8 | rpc DeleteTodo (Id) returns (Empty) {} 9 | rpc UpdateTodo (Todo) returns (Empty) {} 10 | } 11 | 12 | message Empty {} 13 | 14 | message TodoList { 15 | repeated Todo todos = 1; 16 | } 17 | 18 | message Todo { 19 | int32 id = 1; 20 | string task = 2; 21 | } 22 | 23 | message Id { 24 | int32 id = 1; 25 | } -------------------------------------------------------------------------------- /example/server/protos/todos.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package todos; 4 | 5 | service TodoApp { 6 | rpc GetTodos (Empty) returns (TodoList) {} 7 | rpc AddTodo (Todo) returns (Empty) {} 8 | rpc DeleteTodo (Id) returns (Empty) {} 9 | rpc UpdateTodo (Todo) returns (Empty) {} 10 | } 11 | 12 | message Empty {} 13 | 14 | message TodoList { 15 | repeated Todo todos = 1; 16 | } 17 | 18 | message Todo { 19 | int32 id = 1; 20 | string task = 2; 21 | } 22 | 23 | message Id { 24 | int32 id = 1; 25 | } -------------------------------------------------------------------------------- /example/server/src/client_dynamic 2.ts: -------------------------------------------------------------------------------- 1 | import grpc = require('@grpc/grpc-js'); 2 | import loader = require('@grpc/proto-loader'); 3 | import path from 'path'; 4 | 5 | const PROTO_PATH = path.join(__dirname, '../protos/todos.proto'); 6 | 7 | const packageDefinition = loader.loadSync(PROTO_PATH, { 8 | keepCase: true, 9 | longs: String, 10 | enums: String, 11 | defaults: true, 12 | oneofs: true, 13 | }); 14 | 15 | const protoDescriptor = grpc.loadPackageDefinition(packageDefinition); 16 | 17 | const todo_proto = protoDescriptor.todos; // the package 18 | 19 | function main() { 20 | //@ts-ignore 21 | const client = new todo_proto.TodoApp( 22 | 'localhost:3000', 23 | grpc.credentials.createInsecure() 24 | ); 25 | 26 | // @ts-ignore 27 | client.getTodos({}, function (err, response) { 28 | console.log(response); 29 | }); 30 | 31 | client.addTodo( 32 | { 33 | id: 2, 34 | task: 'hello', 35 | }, 36 | // @ts-ignore 37 | function (err, response) {} 38 | ); 39 | } 40 | 41 | main(); 42 | -------------------------------------------------------------------------------- /example/server/src/client_dynamic.ts: -------------------------------------------------------------------------------- 1 | import grpc = require('@grpc/grpc-js'); 2 | import loader = require('@grpc/proto-loader'); 3 | import path from 'path'; 4 | 5 | const PROTO_PATH = path.join(__dirname, '../protos/todos.proto'); 6 | 7 | const packageDefinition = loader.loadSync(PROTO_PATH, { 8 | keepCase: true, 9 | longs: String, 10 | enums: String, 11 | defaults: true, 12 | oneofs: true, 13 | }); 14 | 15 | const protoDescriptor = grpc.loadPackageDefinition(packageDefinition); 16 | 17 | const todo_proto = protoDescriptor.todos; // the package 18 | 19 | function main() { 20 | //@ts-ignore 21 | const client = new todo_proto.TodoApp( 22 | 'localhost:3000', 23 | grpc.credentials.createInsecure() 24 | ); 25 | 26 | // @ts-ignore 27 | client.getTodos({}, function (err, response) { 28 | console.log(response); 29 | }); 30 | 31 | client.addTodo( 32 | { 33 | id: 2, 34 | task: 'hello', 35 | }, 36 | // @ts-ignore 37 | function (err, response) {} 38 | ); 39 | } 40 | 41 | main(); 42 | -------------------------------------------------------------------------------- /example/server/src/client_static 2.ts: -------------------------------------------------------------------------------- 1 | import { ChannelCredentials } from '@grpc/grpc-js'; 2 | import { StocksServiceClient } from '../protos/stocks.grpc-client'; 3 | 4 | function main() { 5 | const client = new StocksServiceClient( 6 | 'localhost:3000', 7 | ChannelCredentials.createInsecure() 8 | ); 9 | 10 | client.getStocks({ username: 'Arthur' }, (err, res) => { 11 | if (err) { 12 | console.error(err); 13 | } else { 14 | console.log(res?.stocks); 15 | return res; 16 | } 17 | }); 18 | } 19 | 20 | main(); 21 | -------------------------------------------------------------------------------- /example/server/src/client_static.ts: -------------------------------------------------------------------------------- 1 | import { ChannelCredentials } from '@grpc/grpc-js'; 2 | import { StocksServiceClient } from '../protos/stocks.grpc-client'; 3 | 4 | function main() { 5 | const client = new StocksServiceClient( 6 | 'localhost:3000', 7 | ChannelCredentials.createInsecure() 8 | ); 9 | 10 | client.getStocks({ username: 'Arthur' }, (err, res) => { 11 | if (err) { 12 | console.error(err); 13 | } else { 14 | console.log(res?.stocks); 15 | return res; 16 | } 17 | }); 18 | } 19 | 20 | main(); 21 | -------------------------------------------------------------------------------- /example/server/src/database/database 2.ts: -------------------------------------------------------------------------------- 1 | import { StocksList, UserStock, User } from '../stocks'; 2 | 3 | const stocksList: StocksList = { 4 | stocks: [ 5 | { 6 | symbol: 'ED', 7 | name: 'ConEdison', 8 | price: 89.38, 9 | time: new Date().toISOString(), 10 | }, 11 | { 12 | symbol: 'APPL', 13 | name: 'Apple', 14 | price: 177.23, 15 | time: new Date().toISOString(), 16 | }, 17 | { 18 | symbol: 'GOOGL', 19 | name: 'Google', 20 | price: 129.08, 21 | time: new Date().toISOString(), 22 | }, 23 | { 24 | symbol: 'MSFT', 25 | name: 'Microsoft', 26 | price: 100.0, 27 | time: new Date().toISOString(), 28 | }, 29 | ], 30 | }; 31 | 32 | const userStocks: UserStock[] = [ 33 | { 34 | username: 'Arthur', 35 | symbol: ['GOOGL'], 36 | }, 37 | { 38 | username: 'Murat', 39 | symbol: ['ED'], 40 | }, 41 | { 42 | username: 'Shiyu', 43 | symbol: ['APPL'], 44 | }, 45 | { 46 | username: 'Jun', 47 | symbol: ['MSFT'], 48 | }, 49 | ]; 50 | 51 | const db = { 52 | query(username: User): Promise { 53 | const user = username.username; 54 | const stocksSet = new Set( 55 | userStocks.filter(e => e.username === user)[0].symbol 56 | ); 57 | const filteredStocksList = stocksList.stocks.filter(e => 58 | stocksSet.has(e.symbol) 59 | ); 60 | 61 | return new Promise(resolve => { 62 | setTimeout(() => { 63 | resolve({ stocks: filteredStocksList }); 64 | }, 2000); 65 | }); 66 | }, 67 | }; 68 | 69 | export default db; 70 | -------------------------------------------------------------------------------- /example/server/src/database/database.ts: -------------------------------------------------------------------------------- 1 | import { StocksList, UserStock, User } from '../stocks'; 2 | 3 | const stocksList: StocksList = { 4 | stocks: [ 5 | { 6 | symbol: 'ED', 7 | name: 'ConEdison', 8 | price: 89.38, 9 | time: new Date().toISOString(), 10 | }, 11 | { 12 | symbol: 'APPL', 13 | name: 'Apple', 14 | price: 177.23, 15 | time: new Date().toISOString(), 16 | }, 17 | { 18 | symbol: 'GOOGL', 19 | name: 'Google', 20 | price: 129.08, 21 | time: new Date().toISOString(), 22 | }, 23 | { 24 | symbol: 'MSFT', 25 | name: 'Microsoft', 26 | price: 100.0, 27 | time: new Date().toISOString(), 28 | }, 29 | ], 30 | }; 31 | 32 | const userStocks: UserStock[] = [ 33 | { 34 | username: 'Arthur', 35 | symbol: ['GOOGL'], 36 | }, 37 | { 38 | username: 'Murat', 39 | symbol: ['ED'], 40 | }, 41 | { 42 | username: 'Shiyu', 43 | symbol: ['APPL'], 44 | }, 45 | { 46 | username: 'Jun', 47 | symbol: ['MSFT'], 48 | }, 49 | ]; 50 | 51 | const db = { 52 | query(username: User): Promise { 53 | const user = username.username; 54 | const stocksSet = new Set( 55 | userStocks.filter(e => e.username === user)[0]?.symbol || [] 56 | ); 57 | const filteredStocksList = stocksList.stocks.filter(e => 58 | stocksSet.has(e.symbol) 59 | ); 60 | 61 | return new Promise(resolve => { 62 | setTimeout(() => { 63 | resolve({ stocks: filteredStocksList }); 64 | }, 2000); 65 | }); 66 | }, 67 | }; 68 | 69 | export default db; 70 | -------------------------------------------------------------------------------- /example/server/src/server_dynamic 2.ts: -------------------------------------------------------------------------------- 1 | import grpc = require('@grpc/grpc-js'); 2 | import loader = require('@grpc/proto-loader'); 3 | import path from 'path'; 4 | 5 | const PROTO_PATH = path.join(__dirname, '../protos/todos.proto'); 6 | 7 | const packageDefinition = loader.loadSync(PROTO_PATH, { 8 | keepCase: true, 9 | longs: String, 10 | enums: String, 11 | defaults: true, 12 | oneofs: true, 13 | }); 14 | 15 | const protoDescriptor = grpc.loadPackageDefinition(packageDefinition); 16 | 17 | const todo_proto = protoDescriptor.todos; // the package 18 | 19 | const todoList = [{ id: 1, task: 'hi' }]; 20 | 21 | // @ts-ignore 22 | // call.request 23 | function getTodos(call, callback) { 24 | // read from cache, if it exists, return cached object 25 | 26 | callback(null, { 27 | todos: todoList, 28 | }); 29 | } 30 | 31 | // @ts-ignore 32 | function addTodo(call, callback) { 33 | // do whatever you need with the payload 34 | todoList.push(call.request); 35 | console.log(todoList); 36 | 37 | // resopnd to client 38 | callback(null, {}); 39 | } 40 | 41 | function main() { 42 | const server = new grpc.Server(); 43 | // @ts-ignore 44 | server.addService(todo_proto.TodoApp.service, { 45 | getTodos, 46 | addTodo, 47 | }); 48 | server.bindAsync( 49 | '0.0.0.0:3000', 50 | grpc.ServerCredentials.createInsecure(), 51 | () => { 52 | server.start(); 53 | } 54 | ); 55 | } 56 | 57 | main(); 58 | -------------------------------------------------------------------------------- /example/server/src/server_dynamic.ts: -------------------------------------------------------------------------------- 1 | import grpc = require('@grpc/grpc-js'); 2 | import loader = require('@grpc/proto-loader'); 3 | import path from 'path'; 4 | 5 | const PROTO_PATH = path.join(__dirname, '../protos/todos.proto'); 6 | 7 | const packageDefinition = loader.loadSync(PROTO_PATH, { 8 | keepCase: true, 9 | longs: String, 10 | enums: String, 11 | defaults: true, 12 | oneofs: true, 13 | }); 14 | 15 | const protoDescriptor = grpc.loadPackageDefinition(packageDefinition); 16 | 17 | const todo_proto = protoDescriptor.todos; // the package 18 | 19 | const todoList = [{ id: 1, task: 'hi' }]; 20 | 21 | // @ts-ignore 22 | // call.request 23 | function getTodos(call, callback) { 24 | // read from cache, if it exists, return cached object 25 | 26 | callback(null, { 27 | todos: todoList, 28 | }); 29 | } 30 | 31 | // @ts-ignore 32 | function addTodo(call, callback) { 33 | // do whatever you need with the payload 34 | todoList.push(call.request); 35 | console.log(todoList); 36 | 37 | // resopnd to client 38 | callback(null, {}); 39 | } 40 | 41 | function main() { 42 | const server = new grpc.Server(); 43 | // @ts-ignore 44 | server.addService(todo_proto.TodoApp.service, { 45 | getTodos, 46 | addTodo, 47 | }); 48 | server.bindAsync( 49 | '0.0.0.0:3000', 50 | grpc.ServerCredentials.createInsecure(), 51 | () => { 52 | server.start(); 53 | } 54 | ); 55 | } 56 | 57 | main(); 58 | -------------------------------------------------------------------------------- /example/server/src/server_static 2.ts: -------------------------------------------------------------------------------- 1 | import { Server, ServerCredentials } from '@grpc/grpc-js'; 2 | 3 | import { 4 | IStocksService, 5 | stocksServiceDefinition, 6 | } from '../protos/stocks.grpc-server'; 7 | 8 | import db from './database/database'; 9 | 10 | function main() { 11 | const stocksService: IStocksService = { 12 | async getStocks(call, callback) { 13 | const stocksList = await db.query(call.request); 14 | console.log(stocksList); 15 | callback(null, stocksList); 16 | }, 17 | addStock() {}, 18 | deleteStock() {}, 19 | }; 20 | 21 | const server = new Server(); 22 | server.addService(stocksServiceDefinition, stocksService); 23 | server.bindAsync( 24 | 'localhost:3000', 25 | ServerCredentials.createInsecure(), 26 | (error, port) => { 27 | if (error) { 28 | console.error(error); 29 | } else { 30 | console.log(`Server is running on port ${port}`); 31 | server.start(); 32 | } 33 | } 34 | ); 35 | } 36 | 37 | main(); 38 | -------------------------------------------------------------------------------- /example/server/src/server_static.ts: -------------------------------------------------------------------------------- 1 | import { Server, ServerCredentials } from '@grpc/grpc-js'; 2 | 3 | import { 4 | IStocksService, 5 | stocksServiceDefinition, 6 | } from '../protos/stocks.grpc-server'; 7 | 8 | import db from './database/database'; 9 | 10 | function main() { 11 | const stocksService: IStocksService = { 12 | async getStocks(call, callback) { 13 | const stocksList = await db.query(call.request); 14 | // console.log(stocksList); 15 | console.log('ran'); 16 | callback(null, stocksList); 17 | }, 18 | addStock() {}, 19 | deleteStock() {}, 20 | }; 21 | 22 | const server = new Server(); 23 | server.addService(stocksServiceDefinition, stocksService); 24 | server.bindAsync( 25 | 'localhost:3000', 26 | ServerCredentials.createInsecure(), 27 | (error, port) => { 28 | if (error) { 29 | console.error(error); 30 | } else { 31 | console.log(`Server is running on port ${port}`); 32 | server.start(); 33 | } 34 | } 35 | ); 36 | } 37 | 38 | main(); 39 | -------------------------------------------------------------------------------- /example/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "ES2015" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs" /* Specify what module code is generated. */, 29 | // "rootDir": "./src" /* Specify the root folder within your source files. */, 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | "rootDirs": [ 34 | "./src", 35 | "./protos" 36 | ] /* Allow multiple folders to be treated as one when resolving modules. */, 37 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 38 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 39 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 40 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 41 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 42 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 43 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 44 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 45 | // "resolveJsonModule": true, /* Enable importing .json files. */ 46 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 47 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 48 | 49 | /* JavaScript Support */ 50 | "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */, 51 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 52 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 53 | 54 | /* Emit */ 55 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 56 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 57 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 58 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 59 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 60 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 61 | "outDir": "./dist" /* Specify an output folder for all emitted files. */, 62 | "removeComments": true /* Disable emitting comments. */, 63 | // "noEmit": true, /* Disable emitting files from a compilation. */ 64 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 65 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 66 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 67 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 68 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 69 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 70 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 71 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 72 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 73 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 74 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 75 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 76 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 77 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 78 | 79 | /* Interop Constraints */ 80 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 81 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 82 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 83 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, 84 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 85 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 86 | 87 | /* Type Checking */ 88 | "strict": true /* Enable all strict type-checking options. */, 89 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 90 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 91 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 92 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 93 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 94 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 95 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 96 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 97 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 98 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 99 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 100 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 101 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 102 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 103 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 104 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 105 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 106 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 107 | 108 | /* Completeness */ 109 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 110 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@grpcexpress/grpcexpress", 3 | "version": "0.4.0", 4 | "description": "", 5 | "private": false, 6 | "main": "./dist/index.js", 7 | "module": "./dist/index.mjs", 8 | "types": "./dist/index.d.ts", 9 | "scripts": { 10 | "dev": "vitest", 11 | "test": "vitest run", 12 | "testCacheStore": "vitest CacheStore", 13 | "lint": "tsc", 14 | "build": "tsup src/index.ts --format cjs,esm --dts", 15 | "ci": "npm run lint && npm run build", 16 | "npm-publish": "npm run lint && npm run build && changeset publish" 17 | }, 18 | "author": "Panda Whales", 19 | "license": "MIT", 20 | "devDependencies": { 21 | "@changesets/cli": "^2.26.2", 22 | "@types/node": "^20.6.0", 23 | "@typescript-eslint/eslint-plugin": "^6.6.0", 24 | "@typescript-eslint/parser": "^6.6.0", 25 | "eslint": "^8.49.0", 26 | "eslint-config-prettier": "^9.0.0", 27 | "jest-environment-jsdom": "^29.7.0", 28 | "tsup": "^7.2.0", 29 | "typescript": "^5.2.2", 30 | "vitest": "^0.34.4" 31 | }, 32 | "dependencies": { 33 | "grpc-web": "^1.4.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/CacheStore.test.ts: -------------------------------------------------------------------------------- 1 | import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; 2 | 3 | import { CacheStore } from "./CacheStore"; 4 | 5 | // Mocking the global localStorage 6 | const mockLocalStorage = { 7 | setItem: vi.fn(), 8 | getItem: vi.fn(), 9 | removeItem: vi.fn(), 10 | clear: vi.fn(), 11 | }; 12 | 13 | global.localStorage = mockLocalStorage as any; 14 | 15 | describe("CacheStore", () => { 16 | // const window: Record = { 17 | // store: {}, 18 | // }; 19 | 20 | let cacheStore: CacheStore; 21 | const key = "testKey"; 22 | const buffer = new Uint8Array([1, 2, 3]); 23 | const mockCacheDuration = 10000; // 10 seconds 24 | const mockCacheSize = 5 * 1024 * 1024; // 5MB 25 | 26 | beforeEach(() => { 27 | vi.useFakeTimers(); 28 | cacheStore = new CacheStore(mockCacheDuration, mockCacheSize); 29 | }); 30 | 31 | afterEach(() => { 32 | vi.restoreAllMocks(); 33 | }); 34 | 35 | it("should be able to subscribe a cache and retrieve it", () => { 36 | cacheStore.subscribe(key, buffer); 37 | expect(cacheStore.get(key)).toEqual(buffer); 38 | }); 39 | 40 | it("should not retrieve an expired buffer", () => { 41 | const subscriptionTime = new Date(); 42 | // Subscribe with a buffer that will expire immediately 43 | cacheStore.subscribe(key, buffer, 0); 44 | // Advance system time to simulate that current time is past the expiration time 45 | vi.setSystemTime(subscriptionTime.getTime() + 10000); 46 | expect(cacheStore.get(key)).toBeUndefined(); 47 | }); 48 | 49 | it("should decrease the currentCapacity when unsubscribed", () => { 50 | cacheStore.subscribe(key, buffer); 51 | const initialCapacity = cacheStore.getCurrentCapacity(); 52 | cacheStore.unsubscribe(key); 53 | expect(cacheStore.getCurrentCapacity()).toBeLessThan(initialCapacity); 54 | }); 55 | 56 | it("should calculate cost correctly", () => { 57 | const initialCall = Date.now(); 58 | const calledCount = 5; 59 | const cost = cacheStore.calculateCost( 60 | initialCall, 61 | buffer.length, 62 | calledCount 63 | ); 64 | const expectedCost = 65 | (1 / buffer.length) * ((calledCount + 1) / (Date.now() - initialCall)); 66 | expect(cost).toBeCloseTo(expectedCost); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /src/CacheStore.ts: -------------------------------------------------------------------------------- 1 | import Heap from './Heap'; 2 | 3 | export type CacheRecord = { 4 | buffer: Uint8Array; 5 | expirationDate: Date; 6 | initialCall: number; // date in ms 7 | size: number; 8 | calledCount: number; 9 | cost: number; 10 | }; 11 | 12 | type Cache = { 13 | data: Map; 14 | cacheDuration: number; 15 | capacity: number; 16 | currentCapacity: number; 17 | }; 18 | 19 | export class CacheStore { 20 | #cache: Cache; 21 | #heap: Heap; 22 | 23 | constructor(cacheDuration: number, cacheSize: number) { 24 | this.#cache = this.initStore(cacheDuration, cacheSize); 25 | this.#heap = new Heap(); 26 | this.loadStore = this.loadStore.bind(this); 27 | this.loadStore(); 28 | this.syncStore = this.syncStore.bind(this); 29 | window.addEventListener('beforeunload', this.syncStore); 30 | } 31 | 32 | initStore(cacheDuration: number, cacheSize: number): Cache { 33 | const data = new Map(); 34 | return { 35 | data: data, 36 | cacheDuration, 37 | capacity: cacheSize, 38 | currentCapacity: 0, 39 | }; 40 | } 41 | 42 | subscribe( 43 | key: string, 44 | buffer: Uint8Array, 45 | cacheDuration: number = this.#cache.cacheDuration 46 | ) { 47 | if (this.#cache.data.has(key)) { 48 | this.#cache.data.delete(key); 49 | } 50 | 51 | // calculate the expected value of the disk size of the stringified buffer 52 | // assuming a normal distribution 53 | const ev = 54 | buffer.length * (10 / 256 + (90 / 256) * 2 + (156 / 256) * 3) + 55 | (buffer.length - 1) + 56 | 2; 57 | // add the size of the key and datetime 58 | const evWithKey = ev + key.length * 2 + 2 + 48; 59 | // increment the currentCapacity to keep track 60 | this.#cache.currentCapacity += evWithKey; 61 | 62 | const expiration = new Date(); 63 | const initialCall = Number(expiration); 64 | expiration.setTime(expiration.getTime() + cacheDuration); 65 | const calledCount = 1; 66 | // initialize the value with frequency 1 67 | const cost = this.calculateCost(initialCall, evWithKey, calledCount); 68 | 69 | this.#cache.data.set(key, { 70 | buffer, 71 | expirationDate: expiration, 72 | initialCall, 73 | size: evWithKey, 74 | calledCount, 75 | cost, 76 | }); 77 | 78 | // if this record will exceed the capacity 79 | // delete records until there is enough space to add the record 80 | while (this.#cache.currentCapacity + evWithKey > this.#cache.capacity) { 81 | // delete returns the size of the deleted record 82 | this.#cache.currentCapacity -= this.#heap.delete(); 83 | } 84 | 85 | this.#heap.insert(this.#cache.data.get(key)!); 86 | } 87 | 88 | unsubscribe(key: string) { 89 | // subtract the size from currentCapacity 90 | const size = this.#cache.data.get(key)?.size || 0; 91 | 92 | this.#cache.currentCapacity -= size; 93 | this.#cache.data.delete(key); 94 | } 95 | 96 | get(key: string) { 97 | const record = this.#cache.data.get(key); 98 | // if the key doesn't exist return 99 | if (!record) return; 100 | // if the cache has expired, delete it and return 101 | if (new Date() > record.expirationDate) { 102 | this.unsubscribe(key); 103 | return; 104 | } 105 | // update the calledCount and heap 106 | record.calledCount += 1; 107 | const oldCost = record.cost; 108 | const newCost = this.calculateCost( 109 | record.initialCall, 110 | record.size, 111 | record.calledCount 112 | ); 113 | record.cost = newCost; 114 | 115 | if (oldCost > newCost) { 116 | this.#heap.heapifyDown(); 117 | } else { 118 | this.#heap.heapifyUp(); 119 | } 120 | 121 | return record.buffer; 122 | } 123 | 124 | getCurrentCapacity(): number { 125 | return this.#cache.currentCapacity; 126 | } 127 | 128 | syncStore() { 129 | // trim the currentCapacity down to 4.8MB 130 | const targetSize = 4.8 * 1024 * 1024; 131 | while (this.#cache.currentCapacity > targetSize) { 132 | this.#cache.currentCapacity -= this.#heap.delete(); 133 | } 134 | 135 | const arr: [string, string, string][] = []; 136 | // iterate through the map, push the key and buffer to an array 137 | this.#cache.data.forEach((v, k) => { 138 | arr.push([k, v.buffer.toString(), v.expirationDate.toISOString()]); 139 | }); 140 | 141 | // stringify the array so it can be saved to local storage 142 | localStorage.setItem('grpcExpressStore', JSON.stringify(arr)); 143 | } 144 | 145 | loadStore() { 146 | const data = localStorage.getItem('grpcExpressStore'); 147 | 148 | if (!data) return; 149 | 150 | const json = JSON.parse(data) as [string, string, string][]; 151 | 152 | json.forEach(array => { 153 | const buffer = new Uint8Array(array[1].split(',').map(e => Number(e))); 154 | // calculate the new expiration 155 | const difference = Number(new Date(array[2])) - Number(new Date()); 156 | if (difference > 0) { 157 | this.subscribe(array[0], buffer, difference); 158 | } 159 | }); 160 | } 161 | 162 | calculateCost(initialCall: number, size: number, calledCount: number) { 163 | calledCount += 1; 164 | const frequency = calledCount / (Number(new Date()) - initialCall); 165 | const value = (1 / size) * frequency; 166 | return value; 167 | } 168 | } 169 | 170 | export default CacheStore; 171 | -------------------------------------------------------------------------------- /src/DeserializerStore.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import deserializerStore from './DeserializerStore'; 4 | 5 | describe('DeserializerStore', () => { 6 | it('should be able to add a deserializer function to the store', () => { 7 | const mockDeserializer = (buffer: Uint8Array) => { 8 | return new TextDecoder().decode(buffer); 9 | }; 10 | const key = 'test'; 11 | deserializerStore.addDeserializer(key, mockDeserializer); 12 | expect(deserializerStore.has(key)).toBe(true); 13 | expect(deserializerStore.has('otherKey')).toBe(false); 14 | }); 15 | 16 | it('should be able to use a deserializer function from the store', () => { 17 | const mockDeserializer = (buffer: Uint8Array) => { 18 | return new TextDecoder().decode(buffer); 19 | }; 20 | const key = 'test'; 21 | deserializerStore.addDeserializer(key, mockDeserializer); 22 | const buffer = new TextEncoder().encode('test value'); 23 | const deserializer = deserializerStore.getDeserializer(key); 24 | expect(deserializer(buffer)).toBe('test value'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/DeserializerStore.ts: -------------------------------------------------------------------------------- 1 | class DeserializerStore { 2 | #store: { [key: string]: (buffer: Uint8Array) => any }; 3 | 4 | constructor() { 5 | this.#store = {}; 6 | } 7 | 8 | addDeserializer(method: string, func: (buffer: Uint8Array) => any) { 9 | this.#store[method] = func; 10 | } 11 | 12 | getDeserializer(method: string) { 13 | return this.#store[method]; 14 | } 15 | 16 | has(method: string) { 17 | if (this.#store[method]) { 18 | return true; 19 | } else { 20 | return false; 21 | } 22 | } 23 | } 24 | 25 | const deserializerStore = new DeserializerStore(); 26 | 27 | export default deserializerStore; 28 | -------------------------------------------------------------------------------- /src/Heap.test.ts: -------------------------------------------------------------------------------- 1 | import { beforeEach, describe, expect, it } from 'vitest'; 2 | 3 | import Heap from './Heap'; 4 | 5 | import { CacheRecord } from './CacheStore'; 6 | 7 | describe('Heap data structure', () => { 8 | const record1: CacheRecord = { 9 | buffer: new Uint8Array([1]), 10 | expirationDate: new Date(2023, 8, 30, 12, 0), 11 | initialCall: Number(new Date(2023, 8, 30, 12, 0)), 12 | size: 10, 13 | calledCount: 2, 14 | cost: 3, 15 | }; 16 | 17 | const record2: CacheRecord = { 18 | buffer: new Uint8Array([1]), 19 | expirationDate: new Date(2023, 8, 30, 12, 0), 20 | initialCall: Number(new Date(2023, 8, 30, 12, 0)), 21 | size: 11, 22 | calledCount: 2, 23 | cost: 10, 24 | }; 25 | 26 | const record3: CacheRecord = { 27 | buffer: new Uint8Array([1]), 28 | expirationDate: new Date(2023, 8, 30, 12, 0), 29 | initialCall: Number(new Date(2023, 8, 30, 12, 0)), 30 | size: 12, 31 | calledCount: 2, 32 | cost: 1, 33 | }; 34 | 35 | let heap: Heap; 36 | 37 | beforeEach(() => { 38 | heap = new Heap(); 39 | }); 40 | 41 | it('should be able to insert a record', () => { 42 | heap.insert(record1); 43 | expect(heap.peak()).toEqual(record1); 44 | }); 45 | 46 | it('should be able to insert a second record with a higher cost', () => { 47 | heap.insert(record1); 48 | heap.insert(record2); 49 | expect(heap.peak()).toEqual(record2); 50 | }); 51 | 52 | it('should be able to insert a second record with a lower cost', () => { 53 | heap.insert(record1); 54 | heap.insert(record3); 55 | expect(heap.peak()).toEqual(record1); 56 | }); 57 | 58 | it('should return the size of the deleted node', () => { 59 | heap.insert(record1); 60 | const val = heap.delete(); 61 | expect(val).toEqual(10); 62 | expect(heap.peak()).toBeUndefined(); 63 | }); 64 | 65 | it('should maintain the heap property after deleting the root', () => { 66 | heap.insert(record1); 67 | heap.insert(record2); 68 | heap.insert(record3); 69 | heap.delete(); 70 | expect(heap.peak()).toEqual(record1); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /src/Heap.ts: -------------------------------------------------------------------------------- 1 | import { CacheRecord } from './CacheStore'; 2 | 3 | class Heap { 4 | #heap: CacheRecord[]; 5 | 6 | constructor() { 7 | this.#heap = []; 8 | } 9 | 10 | peak() { 11 | return this.#heap[0]; 12 | } 13 | 14 | insert(record: CacheRecord) { 15 | this.#heap.push(record); 16 | if (this.#heap.length > 1) { 17 | this.heapifyUp(); 18 | } 19 | } 20 | 21 | delete() { 22 | if (this.#heap.length === 1) { 23 | return this.#heap.pop()!.size; 24 | } 25 | 26 | [this.#heap[0], this.#heap[this.#heap.length - 1]] = [ 27 | this.#heap[this.#heap.length - 1], 28 | this.#heap[0], 29 | ]; 30 | 31 | const value = this.#heap.pop()!.size; 32 | this.heapifyDown(); 33 | return value; 34 | } 35 | 36 | heapifyUp() { 37 | let index = this.#heap.length - 1; 38 | let parent = Math.floor((index - 1) / 2); 39 | 40 | while (index > 0 && this.#heap[parent].cost < this.#heap[index].cost) { 41 | [this.#heap[parent], this.#heap[index]] = [ 42 | this.#heap[index], 43 | this.#heap[parent], 44 | ]; 45 | 46 | index = parent; 47 | parent = Math.floor((index - 1) / 2); 48 | } 49 | } 50 | 51 | heapifyDown() { 52 | let index = 0; 53 | let leftChild = 2 * index + 1; 54 | let rightChild = 2 * index + 2; 55 | const size = this.#heap.length; 56 | 57 | while (leftChild < size) { 58 | // Ensuring the left child index is within the array 59 | let largestChild = leftChild; 60 | 61 | if ( 62 | rightChild < size && 63 | this.#heap[rightChild].cost > this.#heap[leftChild].cost 64 | ) { 65 | largestChild = rightChild; // Right child exists and is greater than left child 66 | } 67 | 68 | if (this.#heap[index].cost >= this.#heap[largestChild].cost) { 69 | break; // Proper place is found 70 | } 71 | 72 | [this.#heap[index], this.#heap[largestChild]] = [ 73 | this.#heap[largestChild], 74 | this.#heap[index], 75 | ]; 76 | 77 | index = largestChild; 78 | leftChild = 2 * index + 1; 79 | rightChild = 2 * index + 2; 80 | } 81 | } 82 | } 83 | 84 | export default Heap; 85 | -------------------------------------------------------------------------------- /src/PendingStore.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import CacheStore from './CacheStore'; 4 | import PendingStore from './PendingStore'; 5 | 6 | describe('PendingStore', () => { 7 | const cacheStore = new CacheStore(600000); 8 | const pendingStore = new PendingStore(cacheStore); 9 | it('should be able to set a function call as pending', () => { 10 | pendingStore.setPending('test'); 11 | expect(pendingStore.has('test')).toEqual(true); 12 | }); 13 | it('should be able to set a function call as done', () => { 14 | pendingStore.setPending('test'); 15 | pendingStore.setDone('test'); 16 | expect(pendingStore.has('test')).toEqual(false); 17 | }); 18 | it('should be able to add a callback to an existing key', async () => { 19 | pendingStore.setPending('test'); 20 | new Promise(resolve => { 21 | pendingStore.addCallback('test', resolve); 22 | }); 23 | expect(pendingStore.has('test')).toEqual(true); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/PendingStore.ts: -------------------------------------------------------------------------------- 1 | import CacheStore from './CacheStore'; 2 | 3 | type item = { 4 | isPending: boolean; 5 | callbacks: any[]; 6 | }; 7 | 8 | class PendingStore { 9 | #store: { [key: string]: item }; 10 | #cacheStore: CacheStore; 11 | 12 | constructor(cacheStore: CacheStore) { 13 | this.#store = {}; 14 | this.#cacheStore = cacheStore; 15 | } 16 | 17 | setPending(key: string) { 18 | this.#store[key] = { 19 | isPending: true, 20 | callbacks: [], 21 | }; 22 | } 23 | 24 | setDone(key: string, deserializer: (buffer: Uint8Array) => unknown) { 25 | // console.log('set done', key); 26 | for (const resolve of this.#store[key].callbacks) { 27 | const cache = this.#cacheStore.get(key); 28 | 29 | if (!cache) resolve(undefined); 30 | 31 | resolve(deserializer(cache!)); 32 | } 33 | delete this.#store[key]; 34 | } 35 | 36 | has(key: string) { 37 | if (this.#store[key]) { 38 | return true; 39 | } else { 40 | return false; 41 | } 42 | } 43 | 44 | addCallback(key: string, callback: any) { 45 | this.#store[key].callbacks.push(callback); 46 | } 47 | } 48 | 49 | export default PendingStore; 50 | -------------------------------------------------------------------------------- /src/grpcExpressClient.test.ts: -------------------------------------------------------------------------------- 1 | import { beforeAll, describe, expect, it } from "vitest"; 2 | import { StocksServiceClient } from "../example/frontend_react/protos/StocksServiceClientPb"; 3 | import { User } from "../example/frontend_react/protos/stocks_pb"; 4 | import grpcExpressClient from "./grpcExpressClient"; 5 | 6 | describe("grpcExpressClient", () => { 7 | let Client; 8 | let user: User; 9 | 10 | beforeAll(() => { 11 | Client = grpcExpressClient(StocksServiceClient, 30000); 12 | user = new User(); 13 | }); 14 | 15 | it("should create a new custom client", () => { 16 | const client = new Client("http://localhost:8080"); 17 | expect(client).toHaveProperty("getStocks"); 18 | }); 19 | 20 | it("should be able to get stock list with existing user", async () => { 21 | const client = new Client("http://localhost:8080"); 22 | user.setUsername("Murat"); 23 | const stocks = await client.getStocks(user, { 24 | cacheOptions: { 25 | cache: "cache", 26 | duration: 10000, 27 | }, 28 | }); 29 | expect(stocks.toObject().stocksList.length).greaterThan(0); 30 | }); 31 | 32 | it("should be able to get empty stock list with non existing user ", async () => { 33 | const client = new Client("http://localhost:8080"); 34 | user.setUsername("Test"); 35 | const stocks = await client.getStocks(user, {}); 36 | expect(stocks.toObject().stocksList.length).toEqual(0); 37 | }); 38 | 39 | it("should be able to return a cached response", async () => { 40 | const client = new Client("http://localhost:8080"); 41 | user.setUsername("Arthur"); 42 | await client.getStocks(user, {}); 43 | const start = new Date(); 44 | await new Promise(resolve => { 45 | setTimeout(resolve, 2000); 46 | }); 47 | await client.getStocks(user, {}); 48 | const end = new Date(); 49 | expect(Number(end) - Number(start)).lessThan(3000); 50 | }); 51 | 52 | it("should be able to pass no cache option", async () => { 53 | const client = new Client("http://localhost:8080"); 54 | user.setUsername("Shiyu"); 55 | await client.getStocks(user, { 56 | cacheOptions: { 57 | cache: "nocache", 58 | }, 59 | }); 60 | const start = new Date(); 61 | await client.getStocks(user, {}); 62 | const end = new Date(); 63 | expect(Number(end) - Number(start)).greaterThan(2000); 64 | }); 65 | 66 | // it('should work with the callback method', () => { 67 | // user.setUsername('Murat'); 68 | // client.getStocks(user, {}, (err: RpcError, res: any) => { 69 | // if (err) { 70 | // return console.log(err); 71 | // } 72 | 73 | // expect(res.toObject().stocksList.length).greaterThan(0); 74 | // }); 75 | // }); 76 | it("should be able to invalidate cached response", async () => { 77 | const Client = grpcExpressClient(StocksServiceClient, 30000); 78 | const client = new Client("http://localhost:8080"); 79 | user.setUsername("Arthur"); 80 | await client.getStocks(user, {}); 81 | client.invalidate('getStocks', user); 82 | const start = new Date(); 83 | await client.getStocks(user, {}); 84 | const end = new Date(); 85 | expect(Number(end) - Number(start)).greaterThan(2000); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /src/grpcExpressClient.ts: -------------------------------------------------------------------------------- 1 | import { RpcError } from 'grpc-web'; 2 | import CacheStore from './CacheStore'; 3 | import deserializerStore from './DeserializerStore'; 4 | import PendingStore from './PendingStore'; 5 | 6 | type MethodNames = { 7 | // eslint-disable-next-line 8 | [K in keyof T]: T[K] extends Function ? K : never; 9 | }[keyof T]; 10 | 11 | export function grpcExpressClient( 12 | constructor: T, 13 | cacheDuration: number = 600000, // defaults to 10 minutes 14 | cacheSize: number = 100 * 1024 * 1024 // default to 100MB in memory cache 15 | ) { 16 | return class extends constructor { 17 | cacheStore: CacheStore; 18 | pendingStore: PendingStore; 19 | constructor(...args: any[]) { 20 | super(...args); 21 | this.cacheStore = new CacheStore(cacheDuration, cacheSize); 22 | this.pendingStore = new PendingStore(this.cacheStore); 23 | // get all functions from the service 24 | const methods = Object.getOwnPropertyNames(constructor.prototype).filter( 25 | prop => prop != 'constructor' 26 | ); 27 | 28 | for (const method of methods) { 29 | const geMethod = async ( 30 | request: any, 31 | metadata?: { [key: string]: string } & { 32 | cacheOptions?: { 33 | cache: string; 34 | duration: number; 35 | }; 36 | }, 37 | callback?: (err: RpcError, response: any) => void 38 | ): Promise => { 39 | const { cacheOptions } = metadata || {}; 40 | delete metadata?.cacheOptions; 41 | 42 | // we do not cache response when called using the callback method 43 | if (callback) { 44 | return constructor.prototype[method].call( 45 | this, 46 | request, 47 | metadata, 48 | callback 49 | ); 50 | } 51 | 52 | // if no cache is passed, skip the caching step 53 | switch (cacheOptions?.cache) { 54 | case 'nocache': 55 | return await constructor.prototype[method].call( 56 | this, 57 | request, 58 | metadata 59 | ); 60 | case 'cache': 61 | break; 62 | default: 63 | break; 64 | } 65 | 66 | const key = `${method}:${request.serializeBinary()}`; 67 | const cache = this.cacheStore.get(key); 68 | 69 | if (cache) { 70 | if (deserializerStore.has(method)) { 71 | const deserialize = deserializerStore.getDeserializer(method); 72 | 73 | return deserialize(cache); 74 | } 75 | } 76 | 77 | let response: any; 78 | 79 | try { 80 | const isPending = this.pendingStore.has(key); 81 | 82 | if (isPending) { 83 | return await new Promise(resolve => { 84 | this.pendingStore.addCallback(key, resolve); 85 | }); 86 | } 87 | 88 | this.pendingStore.setPending(key); 89 | 90 | response = await constructor.prototype[method].call( 91 | this, 92 | request, 93 | metadata 94 | ); 95 | 96 | const serialized = response.serializeBinary(); 97 | this.cacheStore.subscribe( 98 | key, 99 | serialized, 100 | // if a duration is passed in the function call 101 | // override the default duration 102 | cacheOptions?.duration || cacheDuration 103 | ); 104 | 105 | const deserializer = 106 | response.__proto__.constructor.deserializeBinary; 107 | deserializerStore.addDeserializer(method, deserializer); 108 | 109 | this.pendingStore.setDone(key, deserializer); 110 | } catch (e: any) { 111 | response = e as RpcError; 112 | } 113 | 114 | return response; 115 | }; 116 | 117 | this[method] = geMethod; 118 | } 119 | } 120 | invalidate(method: MethodNames>, msg: any) { 121 | const key = `${String(method)}:${msg.serializeBinary()}`; 122 | this.cacheStore.unsubscribe(key); 123 | } 124 | }; 125 | } 126 | 127 | export default grpcExpressClient; 128 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { grpcExpressClient } from './grpcExpressClient'; 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 4 | "module": "commonjs" /* Specify what module code is generated. */, 5 | "rootDir": "./src/" /* Specify the root folder within your source files. */, 6 | "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */, 7 | "checkJs": true /* Enable error reporting in type-checked JavaScript files. */, 8 | "noEmit": true /* Disable emitting files from a compilation. */, 9 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, 10 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 11 | "strict": true /* Enable all strict type-checking options. */, 12 | "noImplicitAny": false /* Enable error reporting for expressions and declarations with an implied 'any' type. */, 13 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 14 | }, 15 | "include": ["src/**/*.ts", "src/**/*.tsx"], 16 | "exclude": ["node_modules", "dist", "**/*.test.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /use_grpc_express/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /use_grpc_express/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint:recommended', 4 | 'plugin:@typescript-eslint/recommended', 5 | 'prettier', 6 | ], 7 | parser: '@typescript-eslint/parser', 8 | plugins: ['@typescript-eslint'], 9 | root: true, 10 | env: { 11 | node: true, 12 | }, 13 | rules: { 14 | '@typescript-eslint/no-explicit-any': 'off', 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /use_grpc_express/.gitignore: -------------------------------------------------------------------------------- 1 | **/**/node_modules 2 | **/**/dist 3 | **/**/.env 4 | **/**/protoc 5 | **/**/.DS_Store 6 | .vscode -------------------------------------------------------------------------------- /use_grpc_express/.npmignore: -------------------------------------------------------------------------------- 1 | .github 2 | src 3 | node_modules 4 | .eslintignore 5 | .eslintrc.cjs 6 | .gitignore 7 | tsconfig.json -------------------------------------------------------------------------------- /use_grpc_express/README.md: -------------------------------------------------------------------------------- 1 | # useGrpcExpress 2 | 3 | ![Version badge](https://img.shields.io/badge/version-1.0.0-blue.svg) 4 | 5 | `useGrpcExpress` is a custom React hook designed to integrate with `gRPCExpress`. 6 | 7 | ## Features 8 | 9 | - **Easy Integration**: Designed to work effortlessly with `gRPCExpress` library. 10 | - **State Management**: Provides loading, error states, response, and any potential error from the method call. 11 | 12 | ## Prerequisites 13 | 14 | Ensure you have the following dependencies installed in your project: 15 | 16 | - `@grpcexpress/grpcexpress` 17 | - `grpc-web` 18 | - `React` 19 | 20 | ## Installation 21 | 22 | To install the `useGrpcExpress` hook, use the following npm command: 23 | 24 | ```bash 25 | npm i @grpcexpress/usegrpcexpress 26 | ``` 27 | 28 | ## Basic Usage 29 | 30 | Incorporate the hook in your React component: 31 | 32 | ```javascript 33 | import { useGrpcExpress } from "@grpcexpress/usegrpcexpress"; 34 | 35 | function MyComponent() { 36 | const { isLoading, isError, data, error } = useGrpcExpress( 37 | client.myMethod, 38 | message 39 | ); 40 | 41 | if (isLoading) return
Loading...
; 42 | if (isError) return
Error: {error.message}
; 43 | 44 | return ( 45 |
46 | {/* Render your data */} 47 | {data &&
{data.someField}
} 48 |
49 | ); 50 | } 51 | ``` 52 | 53 | ## License 54 | 55 | This project is licensed under the MIT License. 56 | 57 | ## Credits 58 | 59 | Junwen Zhang 60 | Arthur Griffith 61 | Murat Agrali 62 | Shi Yu Liu 63 | -------------------------------------------------------------------------------- /use_grpc_express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@grpcexpress/usegrpcexpress", 3 | "version": "0.2.7", 4 | "description": "", 5 | "private": false, 6 | "main": "./dist/index.js", 7 | "module": "./dist/index.mjs", 8 | "types": "./dist/index.d.ts", 9 | "scripts": { 10 | "lint": "tsc", 11 | "build": "tsup src/index.ts --format cjs,esm --dts", 12 | "npm-publish": "npm run build && npm publish --access=public" 13 | }, 14 | "author": "Panda Whales", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@typescript-eslint/eslint-plugin": "^6.6.0", 18 | "@typescript-eslint/parser": "^6.6.0", 19 | "eslint": "^8.49.0", 20 | "eslint-config-prettier": "^9.0.0", 21 | "tsup": "^7.2.0", 22 | "typescript": "^5.2.2" 23 | }, 24 | "peerDependencies": { 25 | "@grpcexpress/grpcexpress": "^0.3.0", 26 | "grpc-web": "^1.4.2", 27 | "react": "^18.2.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /use_grpc_express/src/index.ts: -------------------------------------------------------------------------------- 1 | export {useGrpcExpress} from './useGrpcExpress'; -------------------------------------------------------------------------------- /use_grpc_express/src/useGrpcExpress.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export function useGrpcExpress( 4 | method: (request: Message, metadata: Metadata | null) => any, 5 | message: Message, 6 | metadata?: Metadata 7 | ) { 8 | const [isLoading, setIsLoading] = useState(true); 9 | const [isError, setIsError] = useState(false); 10 | const [data, setData] = useState(null); 11 | const [error, setError] = useState(null); 12 | 13 | useEffect(() => { 14 | method(message, metadata || null) 15 | .then(res => { 16 | setIsLoading(false); 17 | setData(res); 18 | }) 19 | .catch(e => { 20 | setIsError(true); 21 | setError(e); 22 | }); 23 | }, []); 24 | 25 | return { 26 | isLoading, 27 | isError, 28 | data, 29 | error, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /use_grpc_express/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 4 | "module": "commonjs" /* Specify what module code is generated. */, 5 | "rootDir": "./src/" /* Specify the root folder within your source files. */, 6 | "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */, 7 | "checkJs": true /* Enable error reporting in type-checked JavaScript files. */, 8 | "noEmit": true /* Disable emitting files from a compilation. */, 9 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, 10 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 11 | "strict": true /* Enable all strict type-checking options. */, 12 | "noImplicitAny": false /* Enable error reporting for expressions and declarations with an implied 'any' type. */, 13 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 14 | }, 15 | "include": ["src/**/*.ts", "src/**/*.tsx"], 16 | "exclude": ["node_modules", "dist", "**/*.test.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | environment: 'jsdom', 6 | }, 7 | }); 8 | --------------------------------------------------------------------------------