├── postcss.config.js ├── vite.config.js ├── tailwind.config.js ├── src ├── main.jsx ├── index.css ├── App.css ├── App.jsx ├── calculator.client.ts ├── assets │ └── react.svg └── calculator.ts ├── .gitignore ├── index.html ├── README.md ├── protos └── calculator.proto ├── .eslintrc.cjs ├── LICENSE ├── package.json └── public └── vite.svg /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /protos/calculator.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package calculator; 4 | 5 | service Calculator { 6 | rpc Add(CalculationRequest) returns (CalculationResponse); 7 | rpc Divide(CalculationRequest) returns (CalculationResponse); 8 | } 9 | 10 | message CalculationRequest { 11 | int64 a = 1; 12 | int64 b = 2; 13 | } 14 | 15 | message CalculationResponse { int64 result = 1; } 16 | 17 | service Admin { 18 | rpc GetRequestCount(GetCountRequest) returns (CounterResponse); 19 | } 20 | 21 | message GetCountRequest {} 22 | 23 | message CounterResponse { uint64 count = 1; } 24 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 7 | line-height: 1.5; 8 | font-weight: 400; 9 | 10 | color-scheme: dark; 11 | color: rgba(255, 255, 255, 0.87); 12 | background-color: #242424; 13 | 14 | font-synthesis: none; 15 | text-rendering: optimizeLegibility; 16 | -webkit-font-smoothing: antialiased; 17 | -moz-osx-font-smoothing: grayscale; 18 | } 19 | 20 | body { 21 | margin: 0; 22 | display: flex; 23 | place-items: center; 24 | min-width: 320px; 25 | min-height: 100vh; 26 | } 27 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2024 Elliott Minns 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see . 15 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-vue-app", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@protobuf-ts/grpcweb-transport": "^2.9.3", 14 | "@protobuf-ts/plugin": "^2.9.3", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^18.2.55", 20 | "@types/react-dom": "^18.2.19", 21 | "@vitejs/plugin-react": "^4.2.1", 22 | "autoprefixer": "^10.4.17", 23 | "eslint": "^8.56.0", 24 | "eslint-plugin-react": "^7.33.2", 25 | "eslint-plugin-react-hooks": "^4.6.0", 26 | "eslint-plugin-react-refresh": "^0.4.5", 27 | "postcss": "^8.4.35", 28 | "tailwindcss": "^3.4.1", 29 | "vite": "^5.1.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import reactLogo from "./assets/react.svg"; 3 | import viteLogo from "/vite.svg"; 4 | import "./App.css"; 5 | import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport"; 6 | import { CalculatorClient } from "./calculator.client.ts"; 7 | 8 | function App() { 9 | const [num1, setNum1] = useState(0); 10 | const [num2, setNum2] = useState(0); 11 | const [result, setResult] = useState(0); 12 | 13 | let transport = new GrpcWebFetchTransport({ 14 | baseUrl: "http://[::1]:50051", 15 | }); 16 | 17 | const client = new CalculatorClient(transport); 18 | 19 | const handleAdd = () => { 20 | client.add({ a: num1, b: num2 }).then((response) => { 21 | console.log(response.response.result); 22 | setResult(parseInt(response.response.result)); 23 | }); 24 | }; 25 | 26 | return ( 27 | <> 28 |

29 | gRPCalculator 30 |

31 |
32 |
33 | setNum1(e.target.value)} 38 | placeholder="Enter first number" 39 | /> 40 | setNum2(e.target.value)} 45 | placeholder="Enter second number" 46 | /> 47 | 54 |
55 |
56 |
57 |

Result: {result}

58 |
59 | 60 | ); 61 | } 62 | 63 | export default App; 64 | -------------------------------------------------------------------------------- /src/calculator.client.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.9.3 2 | // @generated from protobuf file "calculator.proto" (package "calculator", syntax proto3) 3 | // tslint:disable 4 | import { Admin } from "./calculator"; 5 | import type { CounterResponse } from "./calculator"; 6 | import type { GetCountRequest } from "./calculator"; 7 | import type { RpcTransport } from "@protobuf-ts/runtime-rpc"; 8 | import type { ServiceInfo } from "@protobuf-ts/runtime-rpc"; 9 | import { Calculator } from "./calculator"; 10 | import { stackIntercept } from "@protobuf-ts/runtime-rpc"; 11 | import type { CalculationResponse } from "./calculator"; 12 | import type { CalculationRequest } from "./calculator"; 13 | import type { UnaryCall } from "@protobuf-ts/runtime-rpc"; 14 | import type { RpcOptions } from "@protobuf-ts/runtime-rpc"; 15 | /** 16 | * @generated from protobuf service calculator.Calculator 17 | */ 18 | export interface ICalculatorClient { 19 | /** 20 | * @generated from protobuf rpc: Add(calculator.CalculationRequest) returns (calculator.CalculationResponse); 21 | */ 22 | add(input: CalculationRequest, options?: RpcOptions): UnaryCall; 23 | /** 24 | * @generated from protobuf rpc: Divide(calculator.CalculationRequest) returns (calculator.CalculationResponse); 25 | */ 26 | divide(input: CalculationRequest, options?: RpcOptions): UnaryCall; 27 | } 28 | /** 29 | * @generated from protobuf service calculator.Calculator 30 | */ 31 | export class CalculatorClient implements ICalculatorClient, ServiceInfo { 32 | typeName = Calculator.typeName; 33 | methods = Calculator.methods; 34 | options = Calculator.options; 35 | constructor(private readonly _transport: RpcTransport) { 36 | } 37 | /** 38 | * @generated from protobuf rpc: Add(calculator.CalculationRequest) returns (calculator.CalculationResponse); 39 | */ 40 | add(input: CalculationRequest, options?: RpcOptions): UnaryCall { 41 | const method = this.methods[0], opt = this._transport.mergeOptions(options); 42 | return stackIntercept("unary", this._transport, method, opt, input); 43 | } 44 | /** 45 | * @generated from protobuf rpc: Divide(calculator.CalculationRequest) returns (calculator.CalculationResponse); 46 | */ 47 | divide(input: CalculationRequest, options?: RpcOptions): UnaryCall { 48 | const method = this.methods[1], opt = this._transport.mergeOptions(options); 49 | return stackIntercept("unary", this._transport, method, opt, input); 50 | } 51 | } 52 | /** 53 | * @generated from protobuf service calculator.Admin 54 | */ 55 | export interface IAdminClient { 56 | /** 57 | * @generated from protobuf rpc: GetRequestCount(calculator.GetCountRequest) returns (calculator.CounterResponse); 58 | */ 59 | getRequestCount(input: GetCountRequest, options?: RpcOptions): UnaryCall; 60 | } 61 | /** 62 | * @generated from protobuf service calculator.Admin 63 | */ 64 | export class AdminClient implements IAdminClient, ServiceInfo { 65 | typeName = Admin.typeName; 66 | methods = Admin.methods; 67 | options = Admin.options; 68 | constructor(private readonly _transport: RpcTransport) { 69 | } 70 | /** 71 | * @generated from protobuf rpc: GetRequestCount(calculator.GetCountRequest) returns (calculator.CounterResponse); 72 | */ 73 | getRequestCount(input: GetCountRequest, options?: RpcOptions): UnaryCall { 74 | const method = this.methods[0], opt = this._transport.mergeOptions(options); 75 | return stackIntercept("unary", this._transport, method, opt, input); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/calculator.ts: -------------------------------------------------------------------------------- 1 | // @generated by protobuf-ts 2.9.3 2 | // @generated from protobuf file "calculator.proto" (package "calculator", syntax proto3) 3 | // tslint:disable 4 | import { ServiceType } from "@protobuf-ts/runtime-rpc"; 5 | import type { BinaryWriteOptions } from "@protobuf-ts/runtime"; 6 | import type { IBinaryWriter } from "@protobuf-ts/runtime"; 7 | import { WireType } from "@protobuf-ts/runtime"; 8 | import type { BinaryReadOptions } from "@protobuf-ts/runtime"; 9 | import type { IBinaryReader } from "@protobuf-ts/runtime"; 10 | import { UnknownFieldHandler } from "@protobuf-ts/runtime"; 11 | import type { PartialMessage } from "@protobuf-ts/runtime"; 12 | import { reflectionMergePartial } from "@protobuf-ts/runtime"; 13 | import { MessageType } from "@protobuf-ts/runtime"; 14 | /** 15 | * @generated from protobuf message calculator.CalculationRequest 16 | */ 17 | export interface CalculationRequest { 18 | /** 19 | * @generated from protobuf field: int64 a = 1; 20 | */ 21 | a: bigint; 22 | /** 23 | * @generated from protobuf field: int64 b = 2; 24 | */ 25 | b: bigint; 26 | } 27 | /** 28 | * @generated from protobuf message calculator.CalculationResponse 29 | */ 30 | export interface CalculationResponse { 31 | /** 32 | * @generated from protobuf field: int64 result = 1; 33 | */ 34 | result: bigint; 35 | } 36 | /** 37 | * @generated from protobuf message calculator.GetCountRequest 38 | */ 39 | export interface GetCountRequest { 40 | } 41 | /** 42 | * @generated from protobuf message calculator.CounterResponse 43 | */ 44 | export interface CounterResponse { 45 | /** 46 | * @generated from protobuf field: uint64 count = 1; 47 | */ 48 | count: bigint; 49 | } 50 | // @generated message type with reflection information, may provide speed optimized methods 51 | class CalculationRequest$Type extends MessageType { 52 | constructor() { 53 | super("calculator.CalculationRequest", [ 54 | { no: 1, name: "a", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ }, 55 | { no: 2, name: "b", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ } 56 | ]); 57 | } 58 | create(value?: PartialMessage): CalculationRequest { 59 | const message = globalThis.Object.create((this.messagePrototype!)); 60 | message.a = 0n; 61 | message.b = 0n; 62 | if (value !== undefined) 63 | reflectionMergePartial(this, message, value); 64 | return message; 65 | } 66 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: CalculationRequest): CalculationRequest { 67 | let message = target ?? this.create(), end = reader.pos + length; 68 | while (reader.pos < end) { 69 | let [fieldNo, wireType] = reader.tag(); 70 | switch (fieldNo) { 71 | case /* int64 a */ 1: 72 | message.a = reader.int64().toBigInt(); 73 | break; 74 | case /* int64 b */ 2: 75 | message.b = reader.int64().toBigInt(); 76 | break; 77 | default: 78 | let u = options.readUnknownField; 79 | if (u === "throw") 80 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 81 | let d = reader.skip(wireType); 82 | if (u !== false) 83 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 84 | } 85 | } 86 | return message; 87 | } 88 | internalBinaryWrite(message: CalculationRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 89 | /* int64 a = 1; */ 90 | if (message.a !== 0n) 91 | writer.tag(1, WireType.Varint).int64(message.a); 92 | /* int64 b = 2; */ 93 | if (message.b !== 0n) 94 | writer.tag(2, WireType.Varint).int64(message.b); 95 | let u = options.writeUnknownFields; 96 | if (u !== false) 97 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 98 | return writer; 99 | } 100 | } 101 | /** 102 | * @generated MessageType for protobuf message calculator.CalculationRequest 103 | */ 104 | export const CalculationRequest = new CalculationRequest$Type(); 105 | // @generated message type with reflection information, may provide speed optimized methods 106 | class CalculationResponse$Type extends MessageType { 107 | constructor() { 108 | super("calculator.CalculationResponse", [ 109 | { no: 1, name: "result", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ } 110 | ]); 111 | } 112 | create(value?: PartialMessage): CalculationResponse { 113 | const message = globalThis.Object.create((this.messagePrototype!)); 114 | message.result = 0n; 115 | if (value !== undefined) 116 | reflectionMergePartial(this, message, value); 117 | return message; 118 | } 119 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: CalculationResponse): CalculationResponse { 120 | let message = target ?? this.create(), end = reader.pos + length; 121 | while (reader.pos < end) { 122 | let [fieldNo, wireType] = reader.tag(); 123 | switch (fieldNo) { 124 | case /* int64 result */ 1: 125 | message.result = reader.int64().toBigInt(); 126 | break; 127 | default: 128 | let u = options.readUnknownField; 129 | if (u === "throw") 130 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 131 | let d = reader.skip(wireType); 132 | if (u !== false) 133 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 134 | } 135 | } 136 | return message; 137 | } 138 | internalBinaryWrite(message: CalculationResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 139 | /* int64 result = 1; */ 140 | if (message.result !== 0n) 141 | writer.tag(1, WireType.Varint).int64(message.result); 142 | let u = options.writeUnknownFields; 143 | if (u !== false) 144 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 145 | return writer; 146 | } 147 | } 148 | /** 149 | * @generated MessageType for protobuf message calculator.CalculationResponse 150 | */ 151 | export const CalculationResponse = new CalculationResponse$Type(); 152 | // @generated message type with reflection information, may provide speed optimized methods 153 | class GetCountRequest$Type extends MessageType { 154 | constructor() { 155 | super("calculator.GetCountRequest", []); 156 | } 157 | create(value?: PartialMessage): GetCountRequest { 158 | const message = globalThis.Object.create((this.messagePrototype!)); 159 | if (value !== undefined) 160 | reflectionMergePartial(this, message, value); 161 | return message; 162 | } 163 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: GetCountRequest): GetCountRequest { 164 | return target ?? this.create(); 165 | } 166 | internalBinaryWrite(message: GetCountRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 167 | let u = options.writeUnknownFields; 168 | if (u !== false) 169 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 170 | return writer; 171 | } 172 | } 173 | /** 174 | * @generated MessageType for protobuf message calculator.GetCountRequest 175 | */ 176 | export const GetCountRequest = new GetCountRequest$Type(); 177 | // @generated message type with reflection information, may provide speed optimized methods 178 | class CounterResponse$Type extends MessageType { 179 | constructor() { 180 | super("calculator.CounterResponse", [ 181 | { no: 1, name: "count", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ } 182 | ]); 183 | } 184 | create(value?: PartialMessage): CounterResponse { 185 | const message = globalThis.Object.create((this.messagePrototype!)); 186 | message.count = 0n; 187 | if (value !== undefined) 188 | reflectionMergePartial(this, message, value); 189 | return message; 190 | } 191 | internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: CounterResponse): CounterResponse { 192 | let message = target ?? this.create(), end = reader.pos + length; 193 | while (reader.pos < end) { 194 | let [fieldNo, wireType] = reader.tag(); 195 | switch (fieldNo) { 196 | case /* uint64 count */ 1: 197 | message.count = reader.uint64().toBigInt(); 198 | break; 199 | default: 200 | let u = options.readUnknownField; 201 | if (u === "throw") 202 | throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); 203 | let d = reader.skip(wireType); 204 | if (u !== false) 205 | (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); 206 | } 207 | } 208 | return message; 209 | } 210 | internalBinaryWrite(message: CounterResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { 211 | /* uint64 count = 1; */ 212 | if (message.count !== 0n) 213 | writer.tag(1, WireType.Varint).uint64(message.count); 214 | let u = options.writeUnknownFields; 215 | if (u !== false) 216 | (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); 217 | return writer; 218 | } 219 | } 220 | /** 221 | * @generated MessageType for protobuf message calculator.CounterResponse 222 | */ 223 | export const CounterResponse = new CounterResponse$Type(); 224 | /** 225 | * @generated ServiceType for protobuf service calculator.Calculator 226 | */ 227 | export const Calculator = new ServiceType("calculator.Calculator", [ 228 | { name: "Add", options: {}, I: CalculationRequest, O: CalculationResponse }, 229 | { name: "Divide", options: {}, I: CalculationRequest, O: CalculationResponse } 230 | ]); 231 | /** 232 | * @generated ServiceType for protobuf service calculator.Admin 233 | */ 234 | export const Admin = new ServiceType("calculator.Admin", [ 235 | { name: "GetRequestCount", options: {}, I: GetCountRequest, O: CounterResponse } 236 | ]); 237 | --------------------------------------------------------------------------------