Full Code of grpc/grpc-web for AI

master 1bcab087f1d3 cached
174 files
467.7 KB
123.1k tokens
292 symbols
1 requests
Download .txt
Showing preview only (511K chars total). Download the full file or copy to clipboard to get everything.
Repository: grpc/grpc-web
Branch: master
Commit: 1bcab087f1d3
Files: 174
Total size: 467.7 KB

Directory structure:
gitextract_uo_g1mbd/

├── .bazelci/
│   └── presubmit.yml
├── .bazelignore
├── .bazelrc
├── .bazelrc.windows
├── .dockerignore
├── .github/
│   └── workflows/
│       ├── make-plugin-linux.yml
│       ├── make-plugin-mac-os.yml
│       ├── make-plugin-windows.yml
│       └── release-source-archive.yml
├── .gitignore
├── .gitmodules
├── AUTHORS
├── CHANGELOG.md
├── CODE-OF-CONDUCT.md
├── CONTRIBUTING.md
├── GOVERNANCE.md
├── LICENSE
├── MAINTAINERS.md
├── MODULE.bazel
├── Makefile
├── PATENTS
├── README.md
├── SECURITY.md
├── doc/
│   ├── browser-features.md
│   ├── in-process-proxy.md
│   ├── interop-test-descriptions.md
│   ├── roadmap.md
│   └── streaming-roadmap.md
├── docker-compose.yml
├── etc/
│   ├── localhost.crt
│   └── localhost.key
├── javascript/
│   └── net/
│       └── grpc/
│           └── web/
│               ├── abstractclientbase.js
│               ├── calloptions.js
│               ├── clientoptions.js
│               ├── clientreadablestream.js
│               ├── clientunarycallimpl.js
│               ├── generator/
│               │   ├── BUILD.bazel
│               │   ├── Makefile
│               │   └── grpc_generator.cc
│               ├── generictransportinterface.js
│               ├── grpcwebclientbase.js
│               ├── grpcwebclientbase_test.js
│               ├── grpcwebclientreadablestream.js
│               ├── grpcwebstreamparser.js
│               ├── grpcwebstreamparser_test.js
│               ├── interceptor.js
│               ├── metadata.js
│               ├── methoddescriptor.js
│               ├── methoddescriptorinterface.js
│               ├── methodtype.js
│               ├── request.js
│               ├── requestinternal.js
│               ├── rpcerror.js
│               ├── status.js
│               ├── statuscode.js
│               ├── statuscode_test.js
│               ├── unaryresponse.js
│               └── unaryresponseinternal.js
├── kokoro/
│   ├── interop.cfg
│   ├── master.cfg
│   └── presubmit.cfg
├── net/
│   └── grpc/
│       └── gateway/
│           ├── docker/
│           │   ├── binary_client/
│           │   │   └── Dockerfile
│           │   ├── closure_client/
│           │   │   └── Dockerfile
│           │   ├── commonjs_client/
│           │   │   └── Dockerfile
│           │   ├── echo_server/
│           │   │   └── Dockerfile
│           │   ├── envoy/
│           │   │   └── Dockerfile
│           │   ├── grpcwebproxy/
│           │   │   └── Dockerfile
│           │   ├── interop_client/
│           │   │   └── Dockerfile
│           │   ├── node_interop_server/
│           │   │   └── Dockerfile
│           │   ├── node_server/
│           │   │   └── Dockerfile
│           │   ├── prereqs/
│           │   │   └── Dockerfile
│           │   └── ts_client/
│           │       └── Dockerfile
│           └── examples/
│               ├── echo/
│               │   ├── .gitignore
│               │   ├── BUILD.bazel
│               │   ├── Makefile
│               │   ├── README.md
│               │   ├── commonjs-example/
│               │   │   ├── .gitignore
│               │   │   ├── client.js
│               │   │   ├── echotest.html
│               │   │   ├── package.json
│               │   │   └── webpack.config.js
│               │   ├── echo.proto
│               │   ├── echo_chat.js
│               │   ├── echo_server.cc
│               │   ├── echo_service_impl.cc
│               │   ├── echo_service_impl.h
│               │   ├── echoapp.js
│               │   ├── echotest.html
│               │   ├── envoy.yaml
│               │   ├── node-server/
│               │   │   ├── .gitignore
│               │   │   ├── package.json
│               │   │   └── server.js
│               │   ├── package.json
│               │   ├── ts-example/
│               │   │   ├── .gitignore
│               │   │   ├── README.md
│               │   │   ├── client.ts
│               │   │   ├── echotest.html
│               │   │   ├── package.json
│               │   │   ├── tsconfig.json
│               │   │   └── webpack.config.js
│               │   └── tutorial.md
│               └── helloworld/
│                   ├── .gitignore
│                   ├── README.md
│                   ├── client.js
│                   ├── debugging/
│                   │   └── node-client.js
│                   ├── envoy.yaml
│                   ├── helloworld.proto
│                   ├── index.html
│                   ├── package.json
│                   └── server.js
├── packages/
│   └── grpc-web/
│       ├── .gitignore
│       ├── .npmignore
│       ├── README.md
│       ├── docker/
│       │   └── jsunit-test/
│       │       └── Dockerfile
│       ├── exports.js
│       ├── externs.js
│       ├── gulpfile.js
│       ├── index.d.ts
│       ├── package.json
│       ├── protractor.conf.js
│       ├── protractor_spec.js
│       ├── scripts/
│       │   ├── build.js
│       │   ├── common.py
│       │   ├── gen_all_tests_js.py
│       │   ├── gen_test_htmls.py
│       │   ├── generate_test_files.sh
│       │   ├── run_jsunit_tests.sh
│       │   ├── template_all_tests_js.txt
│       │   └── template_test_html.txt
│       └── test/
│           ├── closure_client.js
│           ├── common.js
│           ├── eval_test.js
│           ├── export_test.js
│           ├── generated_code_test.js
│           ├── gulpfile.js
│           ├── plugin_test.js
│           ├── protos/
│           │   ├── echo.proto
│           │   ├── foo.proto
│           │   ├── models.proto
│           │   ├── myapi/
│           │   │   └── v1/
│           │   │       ├── myapi-two.proto
│           │   │       └── myapi.proto
│           │   ├── nopackage.proto
│           │   ├── otherapi/
│           │   │   └── v1/
│           │   │       └── otherapi.proto
│           │   ├── test01.proto
│           │   ├── test02.proto
│           │   └── test03.proto
│           ├── tsc-tests/
│           │   ├── client01.ts
│           │   ├── client02.ts
│           │   ├── client03.ts
│           │   ├── client04.ts
│           │   ├── client05.ts
│           │   └── client06.ts
│           └── tsc_test.js
├── scripts/
│   ├── README.md
│   ├── docker-run-build-tests.sh
│   ├── docker-run-interop-tests.sh
│   ├── docker-run-jsunit-tests.sh
│   ├── docker-run-mocha-tests.sh
│   ├── init_submodules.sh
│   ├── kokoro.sh
│   ├── release_notes.py
│   ├── run_basic_tests.sh
│   ├── run_interop_tests.sh
│   └── test-proxy.sh
├── src/
│   └── proto/
│       └── grpc/
│           └── testing/
│               ├── empty.proto
│               ├── messages.proto
│               └── test.proto
└── test/
    └── interop/
        ├── .gitignore
        ├── README.md
        ├── envoy.yaml
        ├── index.html
        ├── interop_client.js
        ├── package.json
        └── webpack.config.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .bazelci/presubmit.yml
================================================
---
# TODO(yannic): Enable buildifier and test on Windows and RBE (both unsupported by rules_closure).
platforms:
  macos:
    build_targets:
      - //...
    test_targets:
      - //...

  ubuntu1804:
    build_targets:
      - //...
    test_targets:
      - //...


================================================
FILE: .bazelignore
================================================
# //third_party conatins git submodules.
third_party/


================================================
FILE: .bazelrc
================================================
# Common build settings for unix-like systems
build --copt=-Wno-error=deprecated-declarations
build --host_copt=-Wno-error=deprecated-declarations

# Required until this is the default; expected in Bazel 7
common --enable_bzlmod

# Per-user settings (gitignored). Keep last so local flags can override.
# Docs: https://bazel.build/configure/best-practices#bazelrc
try-import %workspace%/.bazelrc.user


================================================
FILE: .bazelrc.windows
================================================
# This is a Windows-specific bazelrc file for use in GitHub Actions.
# It contains settings from the main .bazelrc file that are relevant for Windows builds.

# Windows specific settings
build --copt=-DABSL_HAVE_WORKING_GCC_WNO_DEPRECATED_DECLARATIONS=0
build --host_copt=-DABSL_HAVE_WORKING_GCC_WNO_DEPRECATED_DECLARATIONS=0

# Required until this is the default; expected in Bazel 7
common --enable_bzlmod

# Per-user settings (gitignored). Keep last so local flags can override.
# Docs: https://bazel.build/configure/best-practices#bazelrc
try-import %workspace%/.bazelrc.user


================================================
FILE: .dockerignore
================================================
**/dist
**/node_modules
packages/grpc-web/generated


================================================
FILE: .github/workflows/make-plugin-linux.yml
================================================
name: Make Linux Plugin

on:
  push:
    paths:
      - .github/workflows/make-plugin-linux.yml
  pull_request:
    paths:
      - .github/workflows/make-plugin-linux.yml
  workflow_dispatch:
    inputs:
      version_number:
        description: 'Version number'
        required: true
        default: '2.x.x'

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-22.04, ubuntu-22.04-arm]
    runs-on: ${{ matrix.os }}
    steps:
    - uses: actions/checkout@v4
    - name: Compute VERSION_NUMBER
      run: |
        INPUT_VERSION="${{ github.event.inputs.version_number }}"
        if [ -n "$INPUT_VERSION" ]; then
          VERSION="$INPUT_VERSION"
        else
          VERSION="$GITHUB_REF_NAME"
        fi
        VERSION="${VERSION//\//-}"
        echo "VERSION_NUMBER=$VERSION" >> "$GITHUB_ENV"
        echo "Computed VERSION_NUMBER=$VERSION"
    - name: Compute ARCH suffix and artifact name
      id: meta
      run: |
        ARCH=$(uname -m)
        case "$ARCH" in
          aarch64|arm64)
            ARCH_SUFFIX="aarch64"
            ;;
          x86_64|amd64)
            ARCH_SUFFIX="x86_64"
            ;;
          *)
            echo "Unsupported architecture: $ARCH" >&2
            exit 1
            ;;
        esac
        ARTIFACT="protoc-gen-grpc-web-${VERSION_NUMBER}-linux-${ARCH_SUFFIX}"
        echo "ARTIFACT=$ARTIFACT" >> "$GITHUB_ENV"
        echo "artifact=$ARTIFACT" >> "$GITHUB_OUTPUT"
        echo "Will produce artifact: $ARTIFACT"
    - name: Install Bazelisk (Bazel)
      run: |
        sudo apt-get update
        sudo apt-get install -y unzip zip
        ARCH=$(uname -m)
        case "$ARCH" in
          aarch64|arm64)
            BAZELISK_URL="https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-arm64"
            ;;
          x86_64|amd64)
            BAZELISK_URL="https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64"
            ;;
          *)
            echo "Unsupported architecture for Bazelisk: $ARCH" >&2
            exit 1
            ;;
        esac
        echo "Downloading Bazelisk from $BAZELISK_URL"
        sudo curl -L -o /usr/local/bin/bazelisk "$BAZELISK_URL"
        sudo chmod +x /usr/local/bin/bazelisk
        # Also provide `bazel` symlink for tools that expect it
        sudo ln -sf /usr/local/bin/bazelisk /usr/local/bin/bazel
        bazelisk version
    - name: Build protoc-gen-grpc-web with Bazel (older glibc baseline)
      run: |
        # Partially static link libstdc++/libgcc to reduce GLIBCXX constraints
        bazelisk build \
          --linkopt=-static-libstdc++ \
          --linkopt=-static-libgcc \
          //javascript/net/grpc/web/generator:protoc-gen-grpc-web
    - name: Move artifact
      run: |
        mv bazel-bin/javascript/net/grpc/web/generator/protoc-gen-grpc-web \
          ./${ARTIFACT}
    - name: Generate sha256
      run: |
        openssl dgst -sha256 -r -out ${ARTIFACT}.sha256 \
          ${ARTIFACT}
    - name: Verify sha256
      run: sha256sum -c ${ARTIFACT}.sha256
    - name: Upload artifacts
      uses: actions/upload-artifact@v4
      with:
        name: ${{ steps.meta.outputs.artifact }}
        path: protoc-gen-grpc-web*


================================================
FILE: .github/workflows/make-plugin-mac-os.yml
================================================
name: Make macOS Plugin

on:
  push:
    paths:
      - .github/workflows/make-plugin-mac-os.yml
  pull_request:
    paths:
      - .github/workflows/make-plugin-mac-os.yml
  workflow_dispatch:
    inputs:
      version_number:
        description: 'Version number'
        required: true
        default: '2.x.x'

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        os: [macos-13, macos-14]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Compute VERSION_NUMBER
        run: |
          INPUT_VERSION="${{ github.event.inputs.version_number }}"
          if [ -n "$INPUT_VERSION" ]; then
            VERSION="$INPUT_VERSION"
          else
            VERSION="$GITHUB_REF_NAME"
          fi
          # Minimal sanitization: replace slashes with dashes to keep filenames valid
          VERSION="${VERSION//\//-}"
          echo "VERSION_NUMBER=$VERSION" >> "$GITHUB_ENV"
          echo "Computed VERSION_NUMBER=$VERSION"
      - name: Compute ARCH suffix and artifact name
        id: meta
        run: |
          ARCH=$(uname -m)
          case "$ARCH" in
            arm64)
              ARCH_SUFFIX="aarch64"
              ;;
            x86_64)
              ARCH_SUFFIX="x86_64"
              ;;
            *)
              echo "Unsupported architecture: $ARCH" >&2
              exit 1
              ;;
          esac
          ARTIFACT="protoc-gen-grpc-web-${VERSION_NUMBER}-darwin-${ARCH_SUFFIX}"
          echo "ARTIFACT=$ARTIFACT" >> "$GITHUB_ENV"
          echo "artifact=$ARTIFACT" >> "$GITHUB_OUTPUT"
          echo "Will produce artifact: $ARTIFACT"
      - name: Install Bazelisk (Bazel)
        run: |
          brew update
          brew install bazelisk
          bazelisk version
      - name: Build protoc-gen-grpc-web with Bazel
        run: |
          bazelisk build //javascript/net/grpc/web/generator:protoc-gen-grpc-web
      - name: Move artifact
        run: |
          mv bazel-bin/javascript/net/grpc/web/generator/protoc-gen-grpc-web \
            ./${ARTIFACT}
      - name: Generate sha256
        run: |
          openssl dgst -sha256 -r -out ${ARTIFACT}.sha256 \
            ${ARTIFACT}
      - name: Verify sha256
        run: shasum -a 256 -c ${ARTIFACT}.sha256
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: ${{ steps.meta.outputs.artifact }}
          path: protoc-gen-grpc-web*


================================================
FILE: .github/workflows/make-plugin-windows.yml
================================================
name: Make Windows Plugin

on:
  push:
    paths:
      - .github/workflows/make-plugin-windows.yml
  pull_request:
    paths:
      - .github/workflows/make-plugin-windows.yml
  workflow_dispatch:
    inputs:
      version_number:
        description: 'Version number'
        required: true
        default: '2.x.x'

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        os: [windows-2025, windows-11-arm]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Compute VERSION_NUMBER
        run: |
          INPUT_VERSION="${{ github.event.inputs.version_number }}"
          if [ -n "$INPUT_VERSION" ]; then
            VERSION="$INPUT_VERSION"
          else
            VERSION="$GITHUB_REF_NAME"
          fi
          # Minimal sanitization: replace slashes with dashes to keep filenames valid
          VERSION="${VERSION//\//-}"
          echo "VERSION_NUMBER=$VERSION" >> "$GITHUB_ENV"
          echo "Computed VERSION_NUMBER=$VERSION"
        shell: bash
      - name: Compute ARCH suffix and artifact name
        id: meta
        shell: powershell
        run: |
          $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
          switch ($arch) {
            'Arm64' { $archSuffix = 'aarch64' }
            'X64'   { $archSuffix = 'x86_64' }
            default { Write-Error "Unsupported architecture: $arch"; exit 1 }
          }
          $artifact = "protoc-gen-grpc-web-$($env:VERSION_NUMBER)-windows-$archSuffix.exe"
          "ARTIFACT=$artifact" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
          "artifact=$artifact" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append
          Write-Host "Will produce artifact: $artifact"
      - name: Install Bazelisk (Bazel)
        shell: powershell
        run: |
          $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
          switch ($arch) {
            'Arm64' { $suffix = 'arm64' }
            'X64'   { $suffix = 'amd64' }
            default { Write-Error "Unsupported architecture: $arch"; exit 1 }
          }
          $url = "https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-windows-$suffix.exe"
          $destDir = "$env:RUNNER_TEMP"
          $dest = Join-Path $destDir 'bazelisk.exe'
          Write-Host "Downloading Bazelisk from $url to $dest"
          Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $dest
          # Add to PATH for subsequent steps
          $destDir | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
      - name: Print Bazelisk version
        shell: powershell
        run: bazelisk version
      - name: Build protoc-gen-grpc-web with Bazel
        run: bazelisk --noworkspace_rc --bazelrc=.bazelrc.windows build //javascript/net/grpc/web/generator:protoc-gen-grpc-web
        shell: powershell
      - name: Move artifact
        run: |
          mv bazel-bin/javascript/net/grpc/web/generator/protoc-gen-grpc-web.exe "./${ARTIFACT}"
        shell: bash
      - name: Generate sha256
        run: |
          openssl dgst -sha256 -r -out "${ARTIFACT}.sha256" "${ARTIFACT}"
        shell: bash
      - name: Verify sha256
        shell: powershell
        run: |
          $shaFile = "${env:ARTIFACT}.sha256"
          if (-not (Test-Path $shaFile)) { Write-Error "SHA256 file not found: $shaFile"; exit 1 }
          $line = Get-Content -Raw $shaFile
          if (-not $line) { Write-Error "Empty sha256 file: $shaFile"; exit 1 }
          $expected = ($line -split '\s+')[0].ToLower()
          $actual = (Get-FileHash "${env:ARTIFACT}" -Algorithm SHA256).Hash.ToLower()
          if ($actual -ne $expected) {
            Write-Error "SHA256 mismatch. Expected $expected, got $actual"
            exit 1
          } else {
            Write-Host "SHA256 verified: $actual"
          }
      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: ${{ steps.meta.outputs.artifact }}
          path: protoc-gen-grpc-web*


================================================
FILE: .github/workflows/release-source-archive.yml
================================================
name: Publish Stable Source Archive

on:
  release:
    types: [published]

jobs:
  # Whenever a release is published, this uploads an accompanying stable source archive.
  #
  # Github doesn't guarantee stability of source archives for more than 6 months[1].
  # More stability is required by projects like Bazel Central Registry[2][3].
  #
  # [1]: https://github.blog/open-source/git/update-on-the-future-stability-of-source-code-archives-and-hashes/
  # [2]: https://github.com/bazelbuild/bazel-central-registry/blob/main/docs/README.md#validations
  # [3]: https://blog.bazel.build/2023/02/15/github-archive-checksum.html
  bazel-release-archive:
    defaults:
        run:
          # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
          shell: /usr/bin/bash -euxo pipefail {0}
    env:
      # github.ref_name is defined here:
      # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context
      TAG: ${{github.ref_name}}
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v3
      # GITHUB_REF is defined here:
      # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
      - run: git archive --format zip    --prefix "grpc-web-$TAG/" --output "grpc-web-source-${TAG}.zip"    "$GITHUB_REF"
      - run: git archive --format tar.gz --prefix "grpc-web-$TAG/" --output "grpc-web-source-${TAG}.tar.gz" "$GITHUB_REF"
      - run: gh release upload "${TAG}" "grpc-web-source-${TAG}.zip" "grpc-web-source-${TAG}.tar.gz"
        env:
          GH_TOKEN: ${{ github.token }}


================================================
FILE: .gitignore
================================================
.vscode
bazel-bin
bazel-genfiles
bazel-grpc-web
bazel-out
bazel-testlogs
*.o
protoc-gen-*
.DS_Store
target
.project
.classpath
.settings
zig-out
zig-cache
.zig-cache


================================================
FILE: .gitmodules
================================================
[submodule "third_party/protobuf"]
	path = third_party/protobuf
	url = https://github.com/protocolbuffers/protobuf.git


================================================
FILE: AUTHORS
================================================
Google Inc.


================================================
FILE: CHANGELOG.md
================================================
[//]: # (GENERATED FILE -- DO NOT EDIT!)
[//]: # (See scripts/release_notes.py for more details.)

## 2.0.2

- [#1507](https://github.com/grpc/grpc-web/pull/1507) Use regular enums for compatibility with TypeScript 5.9+

## 2.0.1

- [#1501](https://github.com/grpc/grpc-web/pull/1501) Update workflow to use Ubuntu 22.04 and static link libstdc++/libgcc

## 2.0.0

### Major Features

- [#1490](https://github.com/grpc/grpc-web/pull/1490) Enable support for Editions in protoc-gen-grpc-web. @Quarke
- [#1445](https://github.com/grpc/grpc-web/pull/1445) Upgrade protobuf to 27.1 and modernize codegen using new APIs @benjaminp

### Other Changes

- [#1496](https://github.com/grpc/grpc-web/pull/1496) Rework release workflows using Github runners (replacing Zig)
- [#1494](https://github.com/grpc/grpc-web/pull/1494) Updates Closure compiler (`20250820.0.0`) and library
- [#1463](https://github.com/grpc/grpc-web/pull/1463) fix: format of typescript definition @nnnnoel
- [#1456](https://github.com/grpc/grpc-web/pull/1456) Bazel bzlmod support for grpc-web. @gonzojive
- [#1452](https://github.com/grpc/grpc-web/pull/1452) Update protobuf-JS `3.14.0` -> `3.21.4`
- [#1441](https://github.com/grpc/grpc-web/pull/1441) Upgrade to Bazel 6.5.0

## 1.5.0

- [#1369](https://github.com/grpc/grpc-web/pull/1369) (Typescript) Mark some `metadata` parameters as optional @andrewmbenton
- [#1335](https://github.com/grpc/grpc-web/pull/1335) Update Debian (and other deps) and remove Java In-process Proxy
- [#1334](https://github.com/grpc/grpc-web/pull/1334) Allow mixed-case headers
- [#1330](https://github.com/grpc/grpc-web/pull/1330) Update ES6 .d.ts imports with comment about corresponding proto import... @gonzojive
- [#1313](https://github.com/grpc/grpc-web/pull/1313) Update ES6 imports with comment about corresponding proto import path. @reddaly

## 1.4.2

- [#1289](https://github.com/grpc/grpc-web/pull/1289) Expose getName() in MethodDescriptor and fix TS definitions.
- [#1230](https://github.com/grpc/grpc-web/pull/1230) GrpcWebClientReadableStream: keep falsy data @pro-wh

## 1.4.1

- [#1286](https://github.com/grpc/grpc-web/pull/1286) Fix duplicate dot in enum name (when "package" is specified)

## 1.4.0

### Major Features

- [#1249](https://github.com/grpc/grpc-web/pull/1249) Use Zig to build aarch64 binaries @hronro
- [#1203](https://github.com/grpc/grpc-web/pull/1203) Github Actions (workflows) for building `protoc-gen-grpc-web` plugins

### Other Changes

- [#1279](https://github.com/grpc/grpc-web/pull/1279) Fixes the status codes ordering in typescript definitions @chandraaditya
- [#1278](https://github.com/grpc/grpc-web/pull/1278) Fix Enum with module in generated TS interface.
- [#1254](https://github.com/grpc/grpc-web/pull/1254) Remove Trailing Slashes from Hostname @jkjk822
- [#1252](https://github.com/grpc/grpc-web/pull/1252) Fix Zig setup step in CI @hronro
- [#1231](https://github.com/grpc/grpc-web/pull/1231) Add version flag and version info in generated code @meling
- [#1225](https://github.com/grpc/grpc-web/pull/1225) Improve error message & Internal code sync
- [#1222](https://github.com/grpc/grpc-web/pull/1222) Update envoy version to 1.22 (with config updates) @tomk9
- [#1211](https://github.com/grpc/grpc-web/pull/1211) Upgrade protobuf and grpc deps @aapeliv
- [#1199](https://github.com/grpc/grpc-web/pull/1199) Revert "Expose MethodDescriptor's public methods"


## 1.3.1

- [#1184](https://github.com/grpc/grpc-web/pull/1184) Correctly support proto3 optional fields in commonjs+dts .d.ts output @mattnathan
- [#1173](https://github.com/grpc/grpc-web/pull/1173) Update envoy version to 1.20
- [#1172](https://github.com/grpc/grpc-web/pull/1172) Fix issue where **no RPC is issued when `deadline` is specified.**
- [#1167](https://github.com/grpc/grpc-web/pull/1167) Fix missing TypeScript return type for `serverStreaming` calls. @lukasmoellerch
- [#1166](https://github.com/grpc/grpc-web/pull/1166) Add missing exports from `RpcError` and add test.
- [#1164](https://github.com/grpc/grpc-web/pull/1164) Add missing class exports @tinrab
- [#1160](https://github.com/grpc/grpc-web/pull/1160) Expose MethodDescriptor's public methods @tomferreira

## 1.3.0

### Major Features

- [#1139](https://github.com/grpc/grpc-web/pull/1139) Improve error type with `RpcError` & internal code sync (contributor: @TomiBelan)
  + (experimental) Typescript users need to update type references from `Error` -> `RpcError`

### Other Changes

- [#1140](https://github.com/grpc/grpc-web/pull/1140) Improve `RpcError.code` typing & internal code sync (contributor:  @richieforeman)
- [#1138](https://github.com/grpc/grpc-web/pull/1138) Remove Bazel in Javascript toolchain
- [#1137](https://github.com/grpc/grpc-web/pull/1137) Revamp Closure JsUnit tests runtime and optimize test/build flows.
- [#1115](https://github.com/grpc/grpc-web/pull/1115) Bump Bazel version -> 4.1.0 and Protobuf version -> 3.17.3
- [#1107](https://github.com/grpc/grpc-web/pull/1107) Allow for custom install prefix @06kellyjac
- [#1063](https://github.com/grpc/grpc-web/pull/1063) Also set timeout on HTTP request if deadline for grpc call is set @Yannic
- [#1004](https://github.com/grpc/grpc-web/pull/1004) Bump closure library version to v20201102
- [#1002](https://github.com/grpc/grpc-web/pull/1002) Bump Envoy version to 1.16.1
- [#998](https://github.com/grpc/grpc-web/pull/998) Fix GrpcWebClientBaseOptions types in index.d.ts @acalvo
- [#971](https://github.com/grpc/grpc-web/pull/971) Add grpc.web.ClientOptions to better document options and add type res... @jennnnny
- [#969](https://github.com/grpc/grpc-web/pull/969) Fix non-determinism in code generator
- [#941](https://github.com/grpc/grpc-web/pull/941) Fix Protobuf .d.ts typings for .proto files without package @Yannic


## 1.2.1

- [#910](https://github.com/grpc/grpc-web/pull/910) Add test to show how to access metadata in interceptor
- [#903](https://github.com/grpc/grpc-web/pull/903) Add error handling to a few error conditions
- [#886](https://github.com/grpc/grpc-web/pull/886) Add missing types definitions
- [#885](https://github.com/grpc/grpc-web/pull/885) Bump Envoy to 1.15.0
- [#884](https://github.com/grpc/grpc-web/pull/884) Update protoc plugin to support Proto3 optional
- [#882](https://github.com/grpc/grpc-web/pull/882) Add @interface MethodDescroptorInterface [@Jennnnny](https://github.com/Jennnnny)
- [#880](https://github.com/grpc/grpc-web/pull/880) Update Bazel to 3.3.1 [@Yannic](https://github.com/Yannic)
- [#874](https://github.com/grpc/grpc-web/pull/874) Add removeListener and missing metadata event types [@danielthank](https://github.com/danielthank)
- [#872](https://github.com/grpc/grpc-web/pull/872) [bazel] Introduce grpc_web_toolchain [@Yannic](https://github.com/Yannic)
- [#871](https://github.com/grpc/grpc-web/pull/871) [generator] Refactor dependency management [@Yannic](https://github.com/Yannic)
- [#869](https://github.com/grpc/grpc-web/pull/869) Add scripts to run interop-tests on grpc-web Java connector


## 1.2.0

### Major Features

- [#847](https://github.com/grpc/grpc-web/pull/847) Allow multiple .on() callbacks and fix issue with non-OK status

### Other Changes

- [#859](https://github.com/grpc/grpc-web/pull/859) Fix envoy.yaml deprecated fields [@dmaixner](https://github.com/dmaixner)
- [#858](https://github.com/grpc/grpc-web/pull/858) Refactor error handling in grpcwebclientbase
- [#857](https://github.com/grpc/grpc-web/pull/857) Migrate to ES6 classes
- [#852](https://github.com/grpc/grpc-web/pull/852) Update to use @grpc/grpc-js node package, and update helloworld exampl...
- [#851](https://github.com/grpc/grpc-web/pull/851) Add a ThenableCall base class for the promise-based unaryCall function [@Jennnnny](https://github.com/Jennnnny)
- [#844](https://github.com/grpc/grpc-web/pull/844) Fix code generator bug and add tests
- [#833](https://github.com/grpc/grpc-web/pull/833) Add proper author attribution to release notes / changelog
- [#827](https://github.com/grpc/grpc-web/pull/827) Splitting callback based client and Promise based client into multiple... [@Jennnnny](https://github.com/Jennnnny)
- [#822](https://github.com/grpc/grpc-web/pull/822) use explicit envoy release tag [@xsbchen](https://github.com/xsbchen)
- [#821](https://github.com/grpc/grpc-web/pull/821) Experimental Feature: Add ES6 import style [@Yannic](https://github.com/Yannic)
- [#738](https://github.com/grpc/grpc-web/pull/738) Avoid double slash in url when client hostname has tailing slash [@hanabi1224](https://github.com/hanabi1224)


## 1.1.0

### Major Features

- [#785](https://github.com/grpc/grpc-web/pull/785) grpc-web interceptors implementation [@Jennnnny](https://github.com/Jennnnny)
- [#772](https://github.com/grpc/grpc-web/pull/772) Add interop test spec and interop tests

### Other Changes

- [#818](https://github.com/grpc/grpc-web/pull/818) All java connector interop tests are passing now
- [#804](https://github.com/grpc/grpc-web/pull/804) Fix a bug in test: callback not properly intercepted
- [#801](https://github.com/grpc/grpc-web/pull/801) Trying to speed up tests
- [#797](https://github.com/grpc/grpc-web/pull/797) Split basic tests with interop tests
- [#780](https://github.com/grpc/grpc-web/pull/780) Add missing separator to imports from external files [@tomiaijo](https://github.com/tomiaijo)
- [#777](https://github.com/grpc/grpc-web/pull/777) Add .on(metadata,...) callback to distinguish initial metadata
- [#764](https://github.com/grpc/grpc-web/pull/764) [generator] Move options parsing into dedicated class [@Yannic](https://github.com/Yannic)
- [#761](https://github.com/grpc/grpc-web/pull/761) Update generic client [@Jennnnny](https://github.com/Jennnnny)
- [#756](https://github.com/grpc/grpc-web/pull/756) Add eval test for TypeScript generated code
- [#752](https://github.com/grpc/grpc-web/pull/752) Disable static checkers on generated js files [@IagoLast](https://github.com/IagoLast)
- [#747](https://github.com/grpc/grpc-web/pull/747) Enable builder pattern in Typescript protobuf messages. [@Orphis](https://github.com/Orphis)
- [#746](https://github.com/grpc/grpc-web/pull/746) Generate Promise based overloads for unary calls in Typescript [@Orphis](https://github.com/Orphis)
- [#745](https://github.com/grpc/grpc-web/pull/745) [bazel] Update rules_closure + fix linter warnings [@Yannic](https://github.com/Yannic)
- [#734](https://github.com/grpc/grpc-web/pull/734) Allow GrpcWebStreamParser to accept Uint8Array [@travikk](https://github.com/travikk)
- [#723](https://github.com/grpc/grpc-web/pull/723) Update bazel version
- [#720](https://github.com/grpc/grpc-web/pull/720) Fix grpcwebproxy interop
- [#716](https://github.com/grpc/grpc-web/pull/716) allow_origin is deprecated in latest envoy server [@noconnor](https://github.com/noconnor)
- [#695](https://github.com/grpc/grpc-web/pull/695) Fix issue 632 (double execution of callback) [@hfinger](https://github.com/hfinger)
- [#692](https://github.com/grpc/grpc-web/pull/692) Do not hardcode CXX to g++


## 1.0.7

- [#671](https://github.com/grpc/grpc-web/pull/671) Add metadata to error callback
- [#668](https://github.com/grpc/grpc-web/pull/668) Remove stream_body.proto
- [#665](https://github.com/grpc/grpc-web/pull/665) Add config for Bazel CI [@Yannic](https://github.com/Yannic)
- [#663](https://github.com/grpc/grpc-web/pull/663) nginx example Expose-Headers add Grpc-Message,Grpc-Status [@zsluedem](https://github.com/zsluedem)
- [#657](https://github.com/grpc/grpc-web/pull/657) Ensure that the end callback is called [@vbfox](https://github.com/vbfox)
- [#655](https://github.com/grpc/grpc-web/pull/655) Use closure compiler from npm in build.js [@vbfox](https://github.com/vbfox)
- [#654](https://github.com/grpc/grpc-web/pull/654) Ignore MacOS .DS_Store files [@vbfox](https://github.com/vbfox)
- [#652](https://github.com/grpc/grpc-web/pull/652) Fix error callback
- [#644](https://github.com/grpc/grpc-web/pull/644) Add CallOptions class [@Jennnnny](https://github.com/Jennnnny)
- [#641](https://github.com/grpc/grpc-web/pull/641) Add/update GOVERNANCE.md and CONTRIBUTING.md
- [#635](https://github.com/grpc/grpc-web/pull/635) Fix generated code return type, and remove unused var
- [#628](https://github.com/grpc/grpc-web/pull/628) Added API for simple unary call [@Jennnnny](https://github.com/Jennnnny)
- [#621](https://github.com/grpc/grpc-web/pull/621) Fix output directory name when using import_style=typescript [@asv](https://github.com/asv)
- [#619](https://github.com/grpc/grpc-web/pull/619) Return specific grpc status code on http error [@harmangakhal](https://github.com/harmangakhal)
- [#618](https://github.com/grpc/grpc-web/pull/618) Generate method descriptors into multiple files [@Jennnnny](https://github.com/Jennnnny)
- [#617](https://github.com/grpc/grpc-web/pull/617) Remove `enabled` deprecated field [@gsalisi](https://github.com/gsalisi)
- [#615](https://github.com/grpc/grpc-web/pull/615) Add support in code generator for printing only method descriptors [@Jennnnny](https://github.com/Jennnnny)
- [#608](https://github.com/grpc/grpc-web/pull/608) Fix status and error callbacks


## 1.0.6

- [#604](https://github.com/grpc/grpc-web/pull/604) Add option to set withCredentials to true
- [#603](https://github.com/grpc/grpc-web/pull/603) Adding some groundwork for generic client [@Jennnnny](https://github.com/Jennnnny)
- [#600](https://github.com/grpc/grpc-web/pull/600) Add generated code eval test
- [#599](https://github.com/grpc/grpc-web/pull/599) fix wrong package name of input type [@lqs](https://github.com/lqs)
- [#593](https://github.com/grpc/grpc-web/pull/593) Fix: Helloworld Example - Enabled Deprecation [@gary-lo](https://github.com/gary-lo)


## 1.0.5

- [#582](https://github.com/grpc/grpc-web/pull/582) Ensure credentials are not undefined in typescript [@Globegitter](https://github.com/Globegitter)
- [#579](https://github.com/grpc/grpc-web/pull/579) Uppercase enum keys in TypeScript definitions [@benfoxbotica](https://github.com/benfoxbotica)
- [#578](https://github.com/grpc/grpc-web/pull/578) Fix depset issues with/upgrade to Bazel 0.27.1 [@factuno-db](https://github.com/factuno-db)
- [#567](https://github.com/grpc/grpc-web/pull/567) Introducing MethodDescriptor [@Jennnnny](https://github.com/Jennnnny)
- [#559](https://github.com/grpc/grpc-web/pull/559) Adding new fields to MethodInfo [@Jennnnny](https://github.com/Jennnnny)
- [#556](https://github.com/grpc/grpc-web/pull/556) Add fix for deadline of strings, NaN, Infinity and -Infinity [@CatEars](https://github.com/CatEars)
- [#546](https://github.com/grpc/grpc-web/pull/546) Changes to deserializeBinary API
- [#540](https://github.com/grpc/grpc-web/pull/540) Method Derserializer should take Uint8Array [@pnegahdar](https://github.com/pnegahdar)
- [#519](https://github.com/grpc/grpc-web/pull/519) remove duplicated has$field$ method for oneof [@yangjian](https://github.com/yangjian)
- [#512](https://github.com/grpc/grpc-web/pull/512) Make client args `credentials` and `options` optional [@jonahbron](https://github.com/jonahbron)


## 1.0.4

- [#502](https://github.com/grpc/grpc-web/pull/502) Attempt to fix flakiness of 'bazel test' [@Yannic](https://github.com/Yannic)
- [#497](https://github.com/grpc/grpc-web/pull/497) Remove a return that skip emission of end callback [@tinou98](https://github.com/tinou98)
- [#494](https://github.com/grpc/grpc-web/pull/494) [bazel] Migrate protobuf info provider to new-style one [@Yannic](https://github.com/Yannic)
- [#482](https://github.com/grpc/grpc-web/pull/482) feature: Typings codegen for bytes field type [@shaxbee](https://github.com/shaxbee)
- [#481](https://github.com/grpc/grpc-web/pull/481) Add module alias to enums for Typescript [@rogchap](https://github.com/rogchap)
- [#460](https://github.com/grpc/grpc-web/pull/460) add typescript definition for Oneof fields [@yangjian](https://github.com/yangjian)
- [#452](https://github.com/grpc/grpc-web/pull/452) fix: exclude map entry message from typings, fix optional values [@shaxbee](https://github.com/shaxbee)
- [#448](https://github.com/grpc/grpc-web/pull/448) Export Map types correctly, optional getter/setters for message types [@shaxbee](https://github.com/shaxbee)
- [#444](https://github.com/grpc/grpc-web/pull/444) feature: Messages in typings extending jspb.Message [@shaxbee](https://github.com/shaxbee)
- [#433](https://github.com/grpc/grpc-web/pull/433) Match name nesting and imports in .d.ts with .js files [@shaxbee](https://github.com/shaxbee)
- [#430](https://github.com/grpc/grpc-web/pull/430) Use camelCase in AsObject definition [@johanbrandhorst](https://github.com/johanbrandhorst)
- [#429](https://github.com/grpc/grpc-web/pull/429) Fix type error in serverStreaming method [@johanbrandhorst](https://github.com/johanbrandhorst)
- [#427](https://github.com/grpc/grpc-web/pull/427) Promise function should use ES5 functions rather than fat arrows (IE b... [@rogchap](https://github.com/rogchap)
- [#422](https://github.com/grpc/grpc-web/pull/422) Enable ADVANCED_OPTIMIZATIONS in Closure Compiler [@jjbubudi](https://github.com/jjbubudi)
- [#421](https://github.com/grpc/grpc-web/pull/421) [bazel] Upgrade to 0.22.0 [@Yannic](https://github.com/Yannic)
- [#413](https://github.com/grpc/grpc-web/pull/413) Emit status event on empty stream response [@shaxbee](https://github.com/shaxbee)
- [#409](https://github.com/grpc/grpc-web/pull/409) Fix metadata typings for TS client [@bpicolo](https://github.com/bpicolo)
- [#404](https://github.com/grpc/grpc-web/pull/404) Generate Typescript definition for top level Enums [@rogchap](https://github.com/rogchap)


## 1.0.3

- [#391](https://github.com/grpc/grpc-web/pull/391) A script to compile protoc plugin
- [#385](https://github.com/grpc/grpc-web/pull/385) Codegen: Support nested types and enums [@shaxbee](https://github.com/shaxbee)
- [#368](https://github.com/grpc/grpc-web/pull/368) Make the bazel rules work with current rules_closure. [@factuno-db](https://github.com/factuno-db)
- [#367](https://github.com/grpc/grpc-web/pull/367) update examples to use addService [@mitchdraft](https://github.com/mitchdraft)
- [#365](https://github.com/grpc/grpc-web/pull/365) Fix response header value with colon
- [#362](https://github.com/grpc/grpc-web/pull/362) Fix the method name clashes for generated commonjs files  [@weilip1803](https://github.com/weilip1803)
- [#360](https://github.com/grpc/grpc-web/pull/360) Fix the import path for generated typescript files [@at-ishikawa](https://github.com/at-ishikawa)


## 1.0.2


## 1.0.1

- [#354](https://github.com/grpc/grpc-web/pull/354) [dts] Generate PromiseClient type definitions in d.ts file [@at-ishikawa](https://github.com/at-ishikawa)
- [#352](https://github.com/grpc/grpc-web/pull/352)  Add a max grpc timeout to the echo example.  [@mjduijn](https://github.com/mjduijn)
- [#348](https://github.com/grpc/grpc-web/pull/348) Fix output dts about 'repeated' for --grpc-web_out=import_style=common... [@rybbchao](https://github.com/rybbchao)
- [#345](https://github.com/grpc/grpc-web/pull/345) update typescript generation to work in strict mode [@henriiik](https://github.com/henriiik)
- [#330](https://github.com/grpc/grpc-web/pull/330) Use official rules_closure repository [@Yannic](https://github.com/Yannic)


## 1.0.0

- [#314](https://github.com/grpc/grpc-web/pull/314) Add a unit test for proto with no package
- [#313](https://github.com/grpc/grpc-web/pull/313) Show how deadline can be set
- [#311](https://github.com/grpc/grpc-web/pull/311) Document how to prevent Envoy to timeout streaming [@mitar](https://github.com/mitar)
- [#310](https://github.com/grpc/grpc-web/pull/310) Correctly generate code if package name is empty [@mitar](https://github.com/mitar)
- [#304](https://github.com/grpc/grpc-web/pull/304) Add a simple Hello World Guide
- [#303](https://github.com/grpc/grpc-web/pull/303) Error code should be number
- [#276](https://github.com/grpc/grpc-web/pull/276) Fix plugin compile error
- [#272](https://github.com/grpc/grpc-web/pull/272) Fix cpp warnings


## 0.4.0

- [#263](https://github.com/grpc/grpc-web/pull/263) Make "Quick" start quicker
- [#258](https://github.com/grpc/grpc-web/pull/258) Experimental Typescript support
- [#257](https://github.com/grpc/grpc-web/pull/257) Fix bug with button in example


## 0.3.0

- [#249](https://github.com/grpc/grpc-web/pull/249) Various fixes to codegen plugin
- [#247](https://github.com/grpc/grpc-web/pull/247) Add generated code unit test
- [#240](https://github.com/grpc/grpc-web/pull/240) webpack demo
- [#239](https://github.com/grpc/grpc-web/pull/239) Expose response metadata for unary calls
- [#219](https://github.com/grpc/grpc-web/pull/219) Add bazel rule closure_grpc_web_library [@Yannic](https://github.com/Yannic)
- [#217](https://github.com/grpc/grpc-web/pull/217) Added multiple proxies interoperability


## 0.2.0

- [#212](https://github.com/grpc/grpc-web/pull/212) Added commonjs-example Dockerfile
- [#211](https://github.com/grpc/grpc-web/pull/211) commonjs support with import_style option [@zaucy](https://github.com/zaucy)
- [#210](https://github.com/grpc/grpc-web/pull/210) grpcweb npm runtime module [@zaucy](https://github.com/zaucy)
- [#209](https://github.com/grpc/grpc-web/pull/209) Add bazel integration and tests
- [#206](https://github.com/grpc/grpc-web/pull/206) Surface underlying XHR errors better
- [#185](https://github.com/grpc/grpc-web/pull/185) Support for proto files without packages [@zaucy](https://github.com/zaucy)


================================================
FILE: CODE-OF-CONDUCT.md
================================================
## Community Code of Conduct

gRPC-Web follows the
[CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).


================================================
FILE: CONTRIBUTING.md
================================================
# How to contribute

We definitely welcome patches and contribution to gRPC-Web! Here is some guideline
and information about how to do so.

Please read the gRPC
organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md)
and [contribution guidelines](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md) before proceeding.

## Getting started

### Legal requirements

In order to protect both you and ourselves, you will need to sign the
[Contributor License Agreement](https://cla.developers.google.com/clas).

### Technical requirements

The basic build script should run to completion.

```sh
$ ./scripts/kokoro.sh
```

More details to come.


================================================
FILE: GOVERNANCE.md
================================================
This repository is governed by the gRPC organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md).


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: MAINTAINERS.md
================================================
This page lists all active maintainers of this repository. If you were a
maintainer and would like to add your name to the Emeritus list, please send us a
PR.

See [GOVERNANCE.md](https://github.com/grpc/grpc-community/blob/master/governance.md)
for governance guidelines and how to become a maintainer.
See [CONTRIBUTING.md](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md)
for general contribution guidelines.

## Maintainers (in alphabetical order)
- [sampajano](https://github.com/sampajano), Google Inc.
- [wenbozhu](https://github.com/wenbozhu), Google Inc.

## Emeritus Maintainers (in alphabetical order)
- [fengli79](https://github.com/fengli79)
- [stanley-cheung](https://github.com/stanley-cheung)


================================================
FILE: MODULE.bazel
================================================
"""
A bazel module for the grpc-web project.

Visit https://grpc.io/ and https://github.com/grpc/grpc-web for
more information about the project.
"""

module(
    name = "grpc-web",
    version = "1.6.0",
    compatibility_level = 1,
    repo_name = "com_github_grpc_grpc_web",
)

bazel_dep(name = "protobuf", version = "27.1", repo_name = "com_google_protobuf")
bazel_dep(name = "grpc", version = "1.65.0", repo_name = "com_github_grpc_grpc")
bazel_dep(name = "rules_cc", version = "0.0.2")
bazel_dep(name = "rules_proto", version = "6.0.2")

# Needed to resolve https://github.com/bazelbuild/bazel-central-registry/issues/2538.
single_version_override(
    module_name = "grpc-java",
    version = "1.64.0",
)


================================================
FILE: Makefile
================================================
ROOT_DIR := $(shell pwd)

all: clean

plugin:
	cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make

install-plugin:
	cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make install

clean:
	cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make clean
	cd "$(ROOT_DIR)"


================================================
FILE: PATENTS
================================================
Additional IP Rights Grant (Patents)

"This implementation" means the copyrightable works distributed by
Google as part of the GRPC project.

Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of GRPC, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of GRPC.  This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation.  If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of GRPC or any code incorporated within this
implementation of GRPC constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of GRPC
shall terminate as of the date such litigation is filed.


================================================
FILE: README.md
================================================
# gRPC Web · [![npm version](https://img.shields.io/npm/v/grpc-web.svg?style=flat)](https://www.npmjs.com/package/grpc-web)

A JavaScript implementation of [gRPC][] for browser clients. For more information,
including a **quick start**, see the [gRPC-web documentation][grpc-web-docs].

gRPC-web clients connect to gRPC services via a special proxy; by default,
gRPC-web uses [Envoy][].

In the future, we expect gRPC-web to be supported in language-specific web
frameworks for languages such as Python, Java, and Node. For details, see the
[roadmap](doc/roadmap.md).

## Streaming Support
gRPC-web currently supports 2 RPC modes:
- Unary RPCs ([example](#make-a-unary-rpc-call))
- Server-side Streaming RPCs ([example](#server-side-streaming)) (NOTE: Only when [`grpcwebtext`](#wire-format-mode) mode is used.)

Client-side and Bi-directional streaming is not currently supported (see [streaming roadmap](doc/streaming-roadmap.md)).

## Quick Start

Eager to get started? Try the [Hello World example][]. From this example, you'll
learn how to do the following:

 - Define your service using protocol buffers
 - Implement a simple gRPC Service using Node.js
 - Configure the Envoy proxy
 - Generate protobuf message classes and client service stub for the client
 - Compile all the JS dependencies into a static library that can be consumed
   by the browser easily

## Advanced Demo: Browser Echo App

You can also try to run a more advanced Echo app from the browser with a
streaming example.

From the repo root directory:

```sh
$ docker-compose pull prereqs node-server envoy commonjs-client
$ docker-compose up node-server envoy commonjs-client
```

Open a browser tab, and visit http://localhost:8081/echotest.html.

To shutdown: `docker-compose down`.

## Runtime Library

The gRPC-web runtime library is available on npm:

```sh
$ npm i grpc-web
```

## Code Generator Plugins

### (Prerequisite) 1. Protobuf (`protoc`)

If you don't already have [`protoc`](https://github.com/protocolbuffers/protobuf)
installed, download it first from [here](https://github.com/protocolbuffers/protobuf/releases) and install it on your PATH.

If you use Homebrew (on macOS), you could run:

```sh
brew install protobuf
```

### (Prerequisite) 2. Protobuf-javascript (`protoc-gen-js`)

If you don't have [`protoc-gen-js`](https://github.com/protocolbuffers/protobuf-javascript) installed, download it from [protocolbuffers/protobuf-javascript](https://github.com/protocolbuffers/protobuf-javascript/releases) and install it on your PATH.

Or, use the [third-party](https://www.npmjs.com/package/protoc-gen-js) npm installer:

```
npm install -g protoc-gen-js
```

### 3. Install gRPC-Web Code Generator

You can download the `protoc-gen-grpc-web` protoc plugin from our
[release](https://github.com/grpc/grpc-web/releases) page:

Make sure all executables are discoverable from your PATH.

For example, on macOS, you can do:

```sh
sudo mv protoc-gen-grpc-web-2.0.2-darwin-aarch64 \
    /usr/local/bin/protoc-gen-grpc-web

chmod +x /usr/local/bin/protoc-gen-grpc-web
```

Note: If you are using our Docker setup, the `prereqs` image already includes
both `protoc` and the `protoc-gen-grpc-web` plugin on the PATH.

### (Optional) 4. Verify Installations

You can optionally verify the plugins work by following our [Hello World example](https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld#generating-stubs):

```sh
cd net/grpc/gateway/examples/helloworld

protoc -I=. helloworld.proto \
  --js_out=import_style=commonjs:. \
  --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.
```

After the command runs successfully, you should now see two new files generated
in the current directory. By running:

```
ls -1 *_pb.js
```

Installation is successful if you see the following 2 files:

 - `helloworld_pb.js` — Generated by `protoc-gen-js` plugin
 - `helloworld_grpc_web_pb.js` - Generated by gRPC-Web plugin

## Client Configuration Options

Typically, you will run the following command to generate the proto messages
and the service client stub from your `.proto` definitions:

```sh
protoc -I=$DIR echo.proto \
  --js_out=import_style=commonjs:$OUT_DIR \
  --grpc-web_out=import_style=commonjs,mode=grpcwebtext:$OUT_DIR
```

You can then use Browserify, Webpack, Closure Compiler, etc. to resolve imports
at compile time.

### Import Style

`import_style=closure`: The default generated code has
[Closure](https://developers.google.com/closure/library/) `goog.require()`
import style.

`import_style=commonjs`: The
[CommonJS](https://requirejs.org/docs/commonjs.html) style `require()` is
also supported.

`import_style=commonjs+dts`: (Experimental) In addition to above, a `.d.ts`
typings file will also be generated for the protobuf messages and service stub.

`import_style=typescript`: (Experimental) The service stub will be generated
in TypeScript. See **TypeScript Support** below for information on how to
generate TypeScript files.

> **Note:** The `commonjs+dts` and `typescript` styles are only supported by
`--grpc-web_out=import_style=...`, not by `--js_out=import_style=...`.

### Wire Format Mode

For more information about the gRPC-web wire format, see the
[specification](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2).

`mode=grpcwebtext`: The default generated code sends the payloads in the
`grpc-web-text` format.

  - `Content-Type: application/grpc-web-text`
  - Payloads are base64-encoded.
  - Both unary and server streaming calls are supported.

`mode=grpcweb`: A binary protobuf format is also supported.

  - `Content-Type: application/grpc-web+proto`
  - Payloads are in the binary protobuf format.
  - Only unary calls are supported.

## How It Works

Let's take a look at how gRPC-web works with a simple example. You can find out
how to build, run and explore the example yourself in
[Build and Run the Echo Example](net/grpc/gateway/examples/echo).

### 1. Define your service

The first step when creating any gRPC service is to define it. Like all gRPC
services, gRPC-web uses
[protocol buffers](https://developers.google.com/protocol-buffers) to define
its RPC service methods and their message request and response types.

```protobuf
message EchoRequest {
  string message = 1;
}

...

service EchoService {
  rpc Echo(EchoRequest) returns (EchoResponse);

  rpc ServerStreamingEcho(ServerStreamingEchoRequest)
      returns (stream ServerStreamingEchoResponse);
}
```

### 2. Run the server and proxy

Next you need to have a gRPC server that implements the service interface and a
gateway proxy that allows the client to connect to the server. Our example
builds a simple Node gRPC backend server and the Envoy proxy.

For the Echo service: see the
[service implementation](net/grpc/gateway/examples/echo/node-server/server.js).

For the Envoy proxy: see the
[config YAML file](net/grpc/gateway/examples/echo/envoy.yaml).

### 3. Write your JS client

Once the server and gateway are up and running, you can start making gRPC calls
from the browser!

Create your client:

```js
var echoService = new proto.mypackage.EchoServiceClient(
  'http://localhost:8080');
```

#### Make a unary RPC call:

```js
var request = new proto.mypackage.EchoRequest();
request.setMessage(msg);
var metadata = {'custom-header-1': 'value1'};
echoService.echo(request, metadata, function(err, response) {
  if (err) {
    console.log(err.code);
    console.log(err.message);
  } else {
    console.log(response.getMessage());
  }
});
```

#### Server-side streaming:

```js
var stream = echoService.serverStreamingEcho(streamRequest, metadata);
stream.on('data', function(response) {
  console.log(response.getMessage());
});
stream.on('status', function(status) {
  console.log(status.code);
  console.log(status.details);
  console.log(status.metadata);
});
stream.on('end', function(end) {
  // stream end signal
});

// to close the stream
stream.cancel();
```

For an in-depth tutorial, see [this
page](net/grpc/gateway/examples/echo/tutorial.md).

## Setting a Deadline

You can set a deadline for your RPC by setting a `deadline` header. The value
should be a Unix timestamp, in milliseconds.

```js
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1);

client.sayHelloAfterDelay(request, {deadline: deadline.getTime().toString()},
  (err, response) => {
    // err will be populated if the RPC exceeds the deadline
    ...
  });
```

## TypeScript Support

The `grpc-web` module can now be imported as a TypeScript module. This is
currently an experimental feature. Any feedback welcome!

When using the `protoc-gen-grpc-web` protoc plugin, mentioned above, pass in
either:

 - `import_style=commonjs+dts`: existing CommonJS style stub + `.d.ts` typings
 - `import_style=typescript`: full TypeScript output

Do *not* use `import_style=typescript` for `--js_out`, it will silently be
ignored. Instead you should use `--js_out=import_style=commonjs`, or
`--js_out=import_style=commonjs,binary` if you are using `mode=grpcweb`. The
`--js_out` plugin will generate JavaScript code (`echo_pb.js`), and the
`-grpc-web_out` plugin will generate a TypeScript definition file for it
(`echo_pb.d.ts`). This is a temporary hack until the `--js_out` supports
TypeScript itself.

For example, this is the command you should use to generate TypeScript code
using the binary wire format:

```sh
protoc -I=$DIR echo.proto \
  --js_out=import_style=commonjs,binary:$OUT_DIR \
  --grpc-web_out=import_style=typescript,mode=grpcweb:$OUT_DIR
```

It will generate the following files:

* `EchoServiceClientPb.ts` - Generated by `--grpc-web_out`, contains the
TypeScript gRPC-web code.
* `echo_pb.js` - Generated by `--js_out`, contains the JavaScript Protobuf
code.
* `echo_pb.d.ts` - Generated by `--grpc-web_out`, contains TypeScript
definitions for `echo_pb.js`.

### Using Callbacks

```ts
import * as grpcWeb from 'grpc-web';
import {EchoServiceClient} from './EchoServiceClientPb';
import {EchoRequest, EchoResponse} from './echo_pb';

const echoService = new EchoServiceClient('http://localhost:8080', null, null);

const request = new EchoRequest();
request.setMessage('Hello World!');

const call = echoService.echo(request, {'custom-header-1': 'value1'},
  (err: grpcWeb.RpcError, response: EchoResponse) => {
    console.log(response.getMessage());
  });
call.on('status', (status: grpcWeb.Status) => {
  // ...
});
```

(See [here](https://github.com/grpc/grpc-web/blob/4d7dc44c2df522376394d3e3315b7ab0e010b0c5/packages/grpc-web/index.d.ts#L29-L39) for the full list of possible `.on(...)` callbacks.)

### (Optional) Using Promises (Limited Features)

> **NOTE:** It is not possible to access the `.on(...)` callbacks (e.g. for `metadata` and `status`) when Promise is used.

```ts
// Create a Promise client instead
const echoService = new EchoServicePromiseClient('http://localhost:8080', null, null);

... (same as above)

this.echoService.echo(request, {'custom-header-1': 'value1'})
  .then((response: EchoResponse) => {
    console.log(`Received response: ${response.getMessage()}`);
  }).catch((err: grpcWeb.RpcError) => {
    console.log(`Received error: ${err.code}, ${err.message}`);
  });
```

For the full TypeScript example, see
[ts-example/client.ts](net/grpc/gateway/examples/echo/ts-example/client.ts) and the [instructions](net/grpc/gateway/examples/echo/ts-example) to run.

## Custom Interceptors

Custom interceptors can be implemented and chained, which could be useful for features like auth, retries, etc.

There are 2 types of interceptors ([interfaces](https://github.com/grpc/grpc-web/blob/3cd7e0d43493d4694fed78400e4ad78031d70c09/packages/grpc-web/index.d.ts#L55-L65)):

- `UnaryInterceptor` ([doc](https://grpc.io/blog/grpc-web-interceptor/#stream-interceptor-example), [example](https://github.com/grpc/grpc-web/blob/master/packages/grpc-web/test/tsc-tests/client04.ts)) - Intercept Unary RPCs; can only be used with Promise clients.
- `StreamInterceptor` ([doc](https://grpc.io/blog/grpc-web-interceptor/#stream-interceptor-example), [example](https://github.com/grpc/grpc-web/blob/master/packages/grpc-web/test/tsc-tests/client03.ts)) - More versatile; can be used with regular clients.

For more details, see [this blog post](https://grpc.io/blog/grpc-web-interceptor/).


## Ecosystem

### Proxy Interoperability

Multiple proxies support the gRPC-web protocol.

1. The current **default proxy** is [Envoy][], which supports gRPC-web out of the box.

	```sh
	$ docker-compose up -d node-server envoy commonjs-client
	```

2. You can also try the [gRPC-web Go Proxy][].

	```sh
	$ docker-compose up -d node-server grpcwebproxy binary-client
	```

3. Apache [APISIX](https://apisix.apache.org/) has also added gRPC-web support, and more details can be found [here](https://apisix.apache.org/blog/2022/01/25/apisix-grpc-web-integration/).

4. [Nginx](https://www.nginx.com/) has a gRPC-web module ([doc](https://nginx.org/en/docs/http/ngx_http_grpc_module.html), [announcement](https://www.nginx.com/blog/nginx-1-13-10-grpc/))), and seems to work with simple configs, according to user [feedback](https://github.com/grpc/grpc-web/discussions/1322).

### Server Frameworks with gRPC-Web support
- [Armeria (JVM)](https://armeria.dev/docs/server-grpc/#grpc-web)
- [Tonic (Rust)](https://docs.rs/tonic-web/latest/tonic_web/)

### Web Frameworks Compatibility
- **Vite** - See this [demo app](https://github.com/a2not/vite-grpc-web), as well as this [comment](https://github.com/grpc/grpc-web/issues/1242#issuecomment-1816249928).

[Envoy]: https://www.envoyproxy.io
[gRPC]: https://grpc.io
[grpc-web-docs]: https://grpc.io/docs/languages/web
[gRPC-web Go Proxy]: https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy
[Hello World example]: net/grpc/gateway/examples/helloworld


================================================
FILE: SECURITY.md
================================================
# Security Policy

For information on gRPC Security Policy and reporting potentional security issues, please see [gRPC CVE Process](https://github.com/grpc/proposal/blob/master/P4-grpc-cve-process.md).


================================================
FILE: doc/browser-features.md
================================================
# gRPC-Web features for browser (HTML) clients

Due to browser limitation, gRPC-Web uses a different transport
than the [HTTP/2 based gRPC protocol](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md).
The difference between the gRPC-Web
protocol and the HTTP/2 based gRPC protocol is specified in the core gRPC repo as [PROTOCOL-WEB](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md). 

In addition to the wire-transport spec, gRPC-Web also supports features that are unique to browser (HTML) clients.
This document serves as the official spec for those features. As the Web platform evolves,
we expect some of those features will evolve too or become deprecated.

On the server-side, [Envoy](https://www.envoyproxy.io/) is the official proxy with built-in gRPC-Web support. New features will be implemented in Envoy first. For [in-process gRPC-Web support](https://github.com/grpc/grpc-web/blob/master/doc/in-process-proxy.md), we recommend that the gRPC-Web module implement only a minimum set of features, e.g. to enable local development. Those features are identified as mandatory features in this doc.

# CORS support

* Should follow the [CORS spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Server-Side_Access_Control) (Mandatory)
  * Access-Control-Allow-Credentials to allow Authorization headers
  * Access-Control-Allow-Methods to allow POST and (preflight) OPTIONS only
  * Access-Control-Allow-Headers to whatever the preflight request carries
  * Access-Control-Expose-Headers to allow Javascript access to `grpc-status,grpc-message` headers.
* The client library is expected to support header overwrites to avoid preflight
  * https://github.com/whatwg/fetch/issues/210
* CSP support to be specified

# HTTP status code mapping

A grpc-web gateway is recommended to overwrite the default 200 status code and map any gateway-generated or server-generated error status to standard HTTP status codes (such as 503) when it is possible. This will help with debugging and may also improve security protection for web apps.

# URL query params

To enable query-param based routing rules in reverse proxies and to avoid CORS preflight, a grpc-web client may "advertise" certain request data or metadata as query params. The grpc-web proxy module should remove the query params before the request is sent to the downstream gRPC server.

The actual data in query params is not interpreted by grpc-web libraries. Standard URL encoding rules shoud be followed to encode those query params.

# Security

* XSRF, XSS policy to be published 

# Compression

* Full-body compression is supported and expected for all unary
requests/responses. The compression/decompression will be done
by browsers, using standard Content-Encoding headers
  * “grpc-encoding” header is not used
  * SDCH, Brotli will be supported
* Message-level compression for streamed requests/responses is not supported
because manual compression/decompression is prohibitively expensive using JS


================================================
FILE: doc/in-process-proxy.md
================================================
# Overview

In-process proxies allow a browser client to talk to a gRPC server directly without relying on any intermediary process
such as an Envoy proxy. This document provides a high-level design guidelines on how we expect such a "proxy" to work.

# The choice of HTTP stack

We strongly recommend that the gRPC-Web module use the default HTTP stack provided by the language platform, or in the case of Java,
the standard Java Servlet framework. This is to ensure maximum portability and to ease integration between gRPC-Web and existing Web
frameworks.

The actual HTTP version that the HTTP stack supports may include both HTTP/1.1 and HTTP/2. In the runtime, it's up to the user-agent and 
intermediaries to negotiate the HTTP version, which is transparent to the gRPC-Web module.

# Request translation

For most languages, the gRPC-Web module will handle the gRPC-Web request, perform the translation, and then proxy the request using a gRPC client 
to the gRPC server via a local socket. The gRPC-Web support is fully transparent to the gRPC server.

For some languages, such as Swift, .NET, if the gRPC server implementation uses the same HTTP stack that the gRPC-Web module uses, then gRPC-Web may be supported 
directly as part of the gRPC server implementation. The added complexity to the gRPC implementation itself is still a concern.

# HTTP port

We expect that gRPC-Web requests are handled on a separate port. If the HTTP stack supports both HTTP/2 and HTTP/1.1, port sharing could be supported. 
However, since CORS is a mandatory feature for gRPC-Web proxies, port sharing should be optional for in-process proxies.

# Core features

The gRPC-Web module should implement only the [core gRPC-Web features](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md) and leave to the HTTP/Web stack provided by the language platform to handle [Web-framework-level features](https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md) such as XSRF, CORS policies. Some of those features may be incompatible with what Envoy supports for gRPC-Web.


================================================
FILE: doc/interop-test-descriptions.md
================================================
gRPC-Web Interop Tests
======================

This document describes the set of tests any gRPC-Web clients or proxies need
to implement. The proto definition for the messages and RPCs we are using for
the tests can be found
[here](https://github.com/grpc/grpc/blob/master/src/proto/grpc/testing/test.proto).

The canonical set of interop tests was defined in the main
[grpc/grpc repo](https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md).

Here in gRPC-Web, we will only implement a subset of tests that's relevant to
gRPC-Web. For example, we will not be implementing any tests involving
client-streaming or bidi-streaming for now. On the other hand, there are
gRPC-Web specific tests that we will add here.

```
 gRPC-Web Client  <-->  Proxy  <--> gRPC Service
```

The idea is that we should be able to swap out any of the 3 components above
and all the interop tests should still pass.

This repository will provide a canonical implementation of the interop test
suite using the Javascript client, Envoy and a gRPC service implemented in
Node.

For any new gRPC-Web client implementation, you need to swap out the JS
client and make sure all tests still pass.

For any in-process proxies implementation, you need to swap out the proxy
and the service as a unit and make sure the standard JS client will still
pass the tests.


List of Tests
-------------

| Test Name | grpc-web-text Mode | grpc-web Binary mode |
| --------- |:------------------:|:--------------------:|
| empty_unary | &#10003; | &#10003; |
| cacheable_unary | TBD | TBD |
| large_unary | &#10003; | &#10003; |
| client_compressed_unary | &#10003; | &#10003; |
| server_compressed_unary | &#10003; | &#10003; |
| client_streaming | &#10007; | &#10007; |
| client_compressed_streaming | &#10007; | &#10007; |
| server_streaming | &#10003; | &#10007; |
| server_compressed_streaming | &#10003; | &#10007; |
| ping_pong | &#10007; | &#10007; |
| empty_stream | &#10007; | &#10007; |
| compute_engine_creds | TBD | TBD |
| jwt_token_creds | TBD | TBD |
| oauth2_auth_token | TBD | TBD |
| per_rpc_creds | TBD | TBD |
| google_default_credentials | TBD | TBD |
| compute_engine_channel_credentials | TBD | TBD |
| custom_metadata * | &#10003; | &#10003; |
| status_code_and_message * | &#10003; | &#10003; |
| special_status_message | &#10003; | &#10003; |
| unimplemented_method | &#10003; | &#10003; |
| unimplemented_service | &#10003; | &#10003; |
| cancel_after_begin | &#10007; | &#10007; |
| cancel_after_first_response | &#10007; | &#10007; |
| timeout_on_sleeping_server | &#10007; | &#10007; |

\* only need to implement the UnaryCall RPC

gRPC-Web specific considerations
--------------------------------

### Text vs Binary mode

As mentioned in the table above, client needs to be tested in both the text
format `application/grpc-web-text` and the binary mode
`application/grpc-web+proto`. The latter we don't need to test any streaming
methods.

### CORS and other web specific scenarios

We may add specific tests to account for web-related scenarios like CORS
handling, etc. Mostly these are to test the connection between the browser
client and the proxy.


================================================
FILE: doc/roadmap.md
================================================
# gRPC-Web Roadmap

The purpose of this document is to collect all the features that we believe are
useful for gRPC users.

## Background

gRPC-Web has been developed internally at Google as part of the front-end
stacks for Google's Web applications and cloud services. Over time we plan to
open-source and publish most of the features and make them available to open-source
users.

Like everywhere, Web platforms and technologies are constantly evolving, often
with many inter-dependent ecosystems. As much as we like to open-source
everything, we also need keep the balance between creating a reusable and stable
open-source solution and meeting those requirements unique to Google's Web applications
(such as search). 

## Roadmap Features

> NOTE: Due to the status of two of gRPC-Web’s core dependencies — [Google
Closure](https://github.com/google/closure-library/issues/1214), which has been
archived, and [Protobuf
JavaScript](https://github.com/protocolbuffers/protobuf-javascript?tab=readme-ov-file#project-status),
which is receiving only minimal updates — the gRPC-Web project is no longer able
to deliver new, modern solutions for the open source community. As a result, we
do not plan to be adding new features going forward.
>
> We recommend you to use [gRPC-Gateway](https://github.com/grpc-ecosystem/grpc-gateway) as an alternative.

### TypeScript Codebase
Migrate the codebase to TypeScript and update the related toolchains (incl. remove
dependency on `closure-compiler`). Enhance overall TypeScript support.

### Streaming Support

Enhance Fetch/streams support (e.g. cancellation support) and improve runtime
support, including service workers.

See streaming roadmap [here](streaming-roadmap.md).

### Non-Binary Message Encoding

The binary protobuf encoding format is not most CPU efficient for browser
clients. Furthermore, the generated code size increases as the total protobuf
definition increases.

For Google's Web applications (e.g. gmail), we use a JSON like format which is
comparable to JSON in efficiency but also very compact in both the message size
and code size.

### Security

We plan to publish a comprehensive guideline doc on how to create secure Web
applications.

Native support such as XSRF, XSS prevention may also be added to the gRPC-Web
protocol.

### Web Framework Integration

This is to provide first-class support for gRPC API and gRPC-Web in popular Web
frameworks such as Angular. 

Note: Dart gRPC will use gRPC-Web as the underlying implementation on the
Dart Web platform.

### Non-Closure compiler support

With the addition of CommonJS style imports, gRPC-Web client stubs can now be
compiled with various tools such as Browserify, Webpack, etc. Let us know
what else we should try!

================================================
FILE: doc/streaming-roadmap.md
================================================
# Streaming Roadmap

This document describes the road-map for gRPC-Web to support different streaming features.
* Server-streaming
* Client-streaming and half-duplex streaming

## Server-streaming

We will keep improving server-streaming in the following areas:
* Fetch cancellation support - 2024
* Performance improvements and whatwg Fetch/streams support, including service workers - 2024
* Finalizing keep-alive support (via Envoy) - 2024+
* Addressing runtime behavior gaps between Fetch and XHR - 2024+

## Client-streaming and half-duplex streaming

We don’t plan to support client-streaming via Fetch/upload-streams (See [Appendix](#chrome-origin-trial-on-upload-streaming) on backgrounds on the Chrome Origin Trial). As a result, half-duplex bidi streaming won’t be supported via Fetch/streams either.

Client-streaming and half-duplex bidi streaming will be addressed when Full-duplex streaming is supported via WebTransport (see below).

## Full-duplex streaming

Not planned.

## Issues with WebSockets

We have no plan to support full-duplex streaming over WebSockets (over TCP or HTTP/2). We will not publish any experimental spec for gRPC over WebSockets either.

The main issue with WebSockets is its incompatibility with HTTP, i.e. the ubiquitous Web infrastructure. This means HTTP fallback is always needed. Recent IETF proposal to tunnel WebSockets over HTTP/2 is not widely implemented either.

## Appendix

### Chrome Origin Trial on `upload-streaming`

We worked on a Chrome [Origin Trial](https://developers.chrome.com/origintrials/#/view_trial/3524066708417413121) 
to finalize the fetch/upload stream API spec (whatwg). One of the pending issues that blocks the final spec is to decide whether it is safe to enable
upload-streaming over HTTP/1.1. We believe that upload-streaming should be enabled for both HTTP/2 and HTTP/1.1. Specifically for gRPC-Web, the server can't control the client deployment. As a result, if upload-streaming is only enabled over HTTP/2, a gRPC service will have to implement a non-streaming method 
as a fallback for each client-streaming method.

================================================
FILE: docker-compose.yml
================================================
version: '3'
services:
  prereqs:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/prereqs/Dockerfile
    image: grpcweb/prereqs
  echo-server:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/echo_server/Dockerfile
    depends_on:
      - prereqs
    image: grpcweb/echo-server
    ports:
      - "9090:9090"
  node-server:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/node_server/Dockerfile
    depends_on:
      - prereqs
    image: grpcweb/node-server
    ports:
      - "9090:9090"
  node-interop-server:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/node_interop_server/Dockerfile
    image: grpcweb/node-interop-server
    ports:
      - "7074:7074"
  envoy:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/envoy/Dockerfile
    image: grpcweb/envoy
    ports:
      - "8080:8080"
    links:
      - node-server
  grpcwebproxy:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/grpcwebproxy/Dockerfile
    image: grpcweb/grpcwebproxy
    ports:
      - "8080:8080"
    links:
      - node-server
  commonjs-client:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/commonjs_client/Dockerfile
    depends_on:
      - prereqs
    image: grpcweb/commonjs-client
    ports:
      - "8081:8081"
  closure-client:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/closure_client/Dockerfile
    depends_on:
      - prereqs
    image: grpcweb/closure-client
    ports:
      - "8081:8081"
  ts-client:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/ts_client/Dockerfile
    depends_on:
      - prereqs
    image: grpcweb/ts-client
    ports:
      - "8081:8081"
  binary-client:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/binary_client/Dockerfile
    depends_on:
      - prereqs
    image: grpcweb/binary-client
    ports:
      - "8081:8081"
  interop-client:
    build:
      context: ./
      dockerfile: ./net/grpc/gateway/docker/interop_client/Dockerfile
    depends_on:
      - prereqs
    image: grpcweb/interop-client
    ports:
      - "8081:8081"
  jsunit-test:
    build:
      context: ./
      dockerfile: ./packages/grpc-web/docker/jsunit-test/Dockerfile
    image: grpcweb/jsunit-test


================================================
FILE: etc/localhost.crt
================================================
-----BEGIN CERTIFICATE-----
MIIC9jCCAd4CCQCfXxHXagE8mjANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQGEwJV
UzELMAkGA1UECAwCQ0ExITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDAeFw0xODA4MDMyMTE2NDdaFw0yMTA1MjMyMTE2NDdaMD0xCzAJBgNVBAYTAlVT
MQswCQYDVQQIDAJDQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2/MlKj+OtIgJm/7DywOR
POypfvGHXqHTpg/ZbZgflx2vMwgoAhdun2e//AlssouStadnkevPEr+uFfxkEzH3
80iYDtcZKXY8E6692hFrp7hKnA7gcBbb7ZQ1FwG/SfKLtLcderLcQb51P7IsQkfh
nB8hSosV9nHhdfVtsMW7L/caqB5lUHIbRsHhSw3+hzg0r0HuKxXd5HlyRXzf9cQX
4xc5B8Ldxo3QmXDOUHDw9quuxvUn5VWppWCGn2J+f9L/5iwgciApbiMBv/CkiVrt
iYwZY+TZY5u8lmL4FtLd2tj2vNXl5ESWcL1SRGSiaYmxX1B5rg4fSAAXmcNOzZHo
8wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCHko8eFag++9knWV8KlRRi+IdGeatU
TejdBVnCPFc7sJf1lkUQSb0mZMv0QEO51aXGVvU46pIjTAwtzcVgPc6ZHqcZY4r3
xscrmECThbhsEQCHqDD55OB2a06bx+ylfbBnLh+F18W+/rI+HlRxSBGclyfVto1P
aCuYvYc0qKK90Ft1joZh1tXpho/D52B4CTa0Ax/5UqSVjTt0uPDhkCZJKnoENVgh
6hF8ehYTC6Kf6ZtbB6+GuaLXf6F96CROLifW219qxrKmGbMyJXolOxLatufnWwwv
Hw7z1FUzulJUkSRmgPJ9hFeyTjCS1BJ18glFjleLykYOtQi8kvnpFm6C
-----END CERTIFICATE-----


================================================
FILE: etc/localhost.key
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA2/MlKj+OtIgJm/7DywORPOypfvGHXqHTpg/ZbZgflx2vMwgo
Ahdun2e//AlssouStadnkevPEr+uFfxkEzH380iYDtcZKXY8E6692hFrp7hKnA7g
cBbb7ZQ1FwG/SfKLtLcderLcQb51P7IsQkfhnB8hSosV9nHhdfVtsMW7L/caqB5l
UHIbRsHhSw3+hzg0r0HuKxXd5HlyRXzf9cQX4xc5B8Ldxo3QmXDOUHDw9quuxvUn
5VWppWCGn2J+f9L/5iwgciApbiMBv/CkiVrtiYwZY+TZY5u8lmL4FtLd2tj2vNXl
5ESWcL1SRGSiaYmxX1B5rg4fSAAXmcNOzZHo8wIDAQABAoIBAFPnJL5BEIb9fezr
+nRvH/BFt0KdkC4hPUOTuDV+Wk6jHDozWk+x8JkOUsYqMjTJ2WVCPtgDRDK6vAXX
CbXo0dUUVC0VEJwoZjJ77iBJlO+d9ZgidKtNjQfMCZSFLhtfUrvVPoGXyT2rEb8C
kK+YDBAqL+DnvbENMBx3SyirxQQ+YetAUSxiZagtjKlax1bhXF/JCj816ezDqOzR
ZVx4MJiJg3oD+zKlwP+IaFlANIuW2W7+LbNzPpdXER4gafRzyjRy5ksO9NFO11Bu
2srJbAMZEr0MCEBjf5rD7CPuvhTTEcgk6CNPsIEt8zzMSZUMeS0xaIv0W1FKiw+R
BENntsECgYEA84fSMRApKaweeBrthPKR1ucnv3EHxn1l1mz45bp+2euHB6jUul9D
629mMv8J9PVdcPF5ck7YpCjslWm6oOhtKMemuwJm61cRF44Lz7jtLm3zNJMGhpbh
6Q0GoIVcyMrW79BODbEIU5SlWp0Usyjo/4CZEP5adho1JNTDHCyvEVUCgYEA5zY6
tbfu+YYgICBnaRkGgdCRBxMurxdPrgbwvrKSMerYE9fufpgvZc5wwx02rJX0psuo
JNGLAkPJyimZCYhY/hxWwXQX8X4BhkKK2aFyBMaDIBA0h+unOwTxHrebQNprot3h
YVT8+tfZf8bGPl+dBs3Qf+WOESjRSyO9jr5BMScCgYBfI4mPF1Qtbot8unBeRvGI
tkeF999kwOp/CZV3EhOqiOP4rxFkOgFrwdp4Q8CdDRpTHFMov/rMrxw2BtcdM5Ap
pU3Ss06H1DzeKeUdYo5uXA/uUx3yiJF7HVagcVldLDkp+QP1P1sUY/bxXnqOv4W/
A3tI80Vd7EEkwWXz5NUD/QKBgGXhYXFdQTI2RcWiQa7v1gwxqRYi/7krXnLioAaH
jR/tyZTE21RxHsGPe+Sd5M+brBgrOUYwBz7SPAKW3dZzfDNMrXXFAB/rVCSjAafw
Gdu81V61hVA3KJM7FDxiz0h+dltnxb4rwuWNY0uIfSZS31B2NF+G+VjaUY74irhx
YSyVAoGANrn70s5+cJDWcmDhaFNU/J/X2Q8GgTyBRd02FIvuv7BXgd9TZ7bUVTws
1nsQCCEVJqj4ksddVRq33NvsnjrgetGYe1LGl3uLakqaXd7iJXFCice3ZuFFrp9o
Iq2sUgG+9K8WFqNhANRKBVd32IpQzjIMAAJSbuG3EFZDLZJqxDs=
-----END RSA PRIVATE KEY-----


================================================
FILE: javascript/net/grpc/web/abstractclientbase.js
================================================
/**
 *
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
/**
 * @fileoverview gRPC browser client library.
 *
 * Base interface for gRPC Web JS clients
 *
 * @author stanleycheung@google.com (Stanley Cheung)
 */
goog.module('grpc.web.AbstractClientBase');

goog.module.declareLegacyNamespace();


const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const MethodDescriptor = goog.require('grpc.web.MethodDescriptor');
const RpcError = goog.require('grpc.web.RpcError');


/**
 * @constructor
 * @struct
 * @final
 */
const PromiseCallOptions = function() {};

/**
 * An AbortSignal to abort the call.
 * @type {AbortSignal|undefined}
 */
PromiseCallOptions.prototype.signal;


/**
 * This interface represents a grpc-web client
 * @interface
 */
const AbstractClientBase = class {
  constructor() {}

  /**
   * @abstract
   * @template REQUEST, RESPONSE
   * @param {string} method The method to invoke
   * @param {REQUEST} requestMessage The request proto
   * @param {!Object<string, string>} metadata User defined call metadata
   * @param {!MethodDescriptor<REQUEST, RESPONSE>}
   *   methodDescriptor Information of this RPC method
   * @param {function(?RpcError, ?)}
   *   callback A callback function which takes (error, RESPONSE or null)
   * @return {!ClientReadableStream<RESPONSE>}
   */
  rpcCall(method, requestMessage, metadata, methodDescriptor, callback) {}

  /**
   * @abstract
   * @protected
   * @template REQUEST, RESPONSE
   * @param {string} method The method to invoke
   * @param {REQUEST} requestMessage The request proto
   * @param {!Object<string, string>} metadata User defined call metadata
   * @param {!MethodDescriptor<REQUEST, RESPONSE>}
   *   methodDescriptor Information of this RPC method
   * @param options Options for the call
   * @return {!IThenable<RESPONSE>}
   *   A promise that resolves to the response message
   */
  thenableCall(method, requestMessage, metadata, methodDescriptor, options) {}

  /**
   * @abstract
   * @template REQUEST, RESPONSE
   * @param {string} method The method to invoke
   * @param {REQUEST} requestMessage The request proto
   * @param {!Object<string, string>} metadata User defined call metadata
   * @param {!MethodDescriptor<REQUEST, RESPONSE>}
   *   methodDescriptor Information of this RPC method
   * @return {!ClientReadableStream<RESPONSE>} The Client Readable Stream
   */
  serverStreaming(method, requestMessage, metadata, methodDescriptor) {}
};

/**
 * Get the hostname of the current request.
 * @template REQUEST, RESPONSE
 * @param {string} method
 * @param {!MethodDescriptor<REQUEST,RESPONSE>} methodDescriptor
 * @return {string}
 */
function getHostname(method, methodDescriptor) {
  // method = hostname + methodDescriptor.name(relative path of this method)
  return method.substr(0, method.length - methodDescriptor.name.length);
}


exports = {AbstractClientBase, PromiseCallOptions, getHostname};


================================================
FILE: javascript/net/grpc/web/calloptions.js
================================================
/**
 * @fileoverview grpc.web.CallOptions
 */

goog.module('grpc.web.CallOptions');
goog.module.declareLegacyNamespace();

/**
 * The collection of runtime options for a new RPC call.
 * @unrestricted
 */
class CallOptions {
  /**
   * @param {!Object<string, !Object>=} options
   */
  constructor(options) {
    /**
     * @const {!Object<string, !Object>}
     * @private
     */
    this.properties_ = options || {};
  }

  /**
   * Add a new CallOption or override an existing one.
   *
   * @param {string} name name of the CallOption that should be
   *     added/overridden.
   * @param {VALUE} value value of the CallOption
   * @template VALUE
   */
  setOption(name, value) {
    this.properties_[name] = value;
  }

  /**
   * Get the value of one CallOption.
   *
   * @param {string} name name of the CallOption.
   * @return {!Object} value of the CallOption. If name doesn't exist, will
   *     return 'undefined'.
   */
  get(name) {
    return this.properties_[name];
  }

  /**
   * Remove a CallOption.
   *
   * @param {string} name name of the CallOption that shoud be removed.
   */
  removeOption(name) {
    delete this.properties_[name];
  }

  /**
   * @return {!Array<string>}
   */
  getKeys() {
    return Object.keys(this.properties_);
  }
}



exports = CallOptions;


================================================
FILE: javascript/net/grpc/web/clientoptions.js
================================================
goog.module('grpc.web.ClientOptions');
goog.module.declareLegacyNamespace();

const {StreamInterceptor, UnaryInterceptor} = goog.require('grpc.web.Interceptor');


/**
 * Options that are available during the client construction.
 * @record
 */
class ClientOptions {
  constructor() {
    /**
     * Whether to use the HttpCors library to pack http headers into a special
     * url query param $httpHeaders= so that browsers can bypass CORS OPTIONS
     * requests.
     * @type {boolean|undefined}
     */
    this.suppressCorsPreflight;

    /**
     * Whether to turn on XMLHttpRequest's withCredentials flag.
     * @type {boolean|undefined}
     */
    this.withCredentials;

    /**
     * Unary interceptors. Note that they are only available in grpcweb and
     * grpcwebtext mode
     * @type {!Array<!UnaryInterceptor>|undefined}
     */
    this.unaryInterceptors;

    /**
     * Stream interceptors. Note that they are only available in grpcweb and
     * grpcwebtext mode
     * @type {!Array<!StreamInterceptor>|undefined}
     */
    this.streamInterceptors;

    /**
     * Protocol buffer format for open source gRPC-Web. This attribute should be
     * specified by the gRPC-Web build rule by default.
     * @type {string|undefined}
     */
    this.format;

    /**
     * The Worker global scope. Once this option is specified, gRPC-Web will
     * also use 'fetch' API as the underlying transport instead of native
     * XmlHttpRequest.
     * @type {!WorkerGlobalScope|undefined}
     */
    this.workerScope;

    /**
     * This is an experimental feature to reduce memory consumption
     * during high throughput server-streaming calls by using
     * 'streamBinaryChunks' mode FetchXmlHttpFactory.
     * @type {boolean|undefined}
     */
    this.useFetchDownloadStreams;
  }
}

exports = ClientOptions;


================================================
FILE: javascript/net/grpc/web/clientreadablestream.js
================================================
/**
 *
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
/**
 * @fileoverview gRPC web client Readable Stream
 *
 * This class is being returned after a gRPC streaming call has been
 * started. This class provides functionality for user to operates on
 * the stream, e.g. set onData callback, etc.
 *
 * This wraps the underlying goog.net.streams.NodeReadableStream
 *
 * @author stanleycheung@google.com (Stanley Cheung)
 */
goog.module('grpc.web.ClientReadableStream');

goog.module.declareLegacyNamespace();



/**
 * A stream that the client can read from. Used for calls that are streaming
 * from the server side.
 *
 * @template RESPONSE
 * @interface
 */
const ClientReadableStream = function() {};


/**
 * Register a callback to handle different stream events.
 *
 * Available event types for gRPC-Web:
 * 'data': The 'data' event is emitted when a new response message chunk is
 * received and successfully handled by gRPC-Web client.
 * 'status': the google RPC status of the response stream.
 * 'end': The 'end' event is emitted when all the data have been successfully
 * consumed from the stream.
 * 'error': typically, this may occur when an  underlying internal failure
 * happens, or a stream implementation attempts to push an invalid chunk of
 * data.
 * 'metadata': the response metadata. Response headers should be read via
 * 'metadata' callbacks.
 *
 * For server-streaming calls. the 'data' and 'status' callbacks (if exist)
 * will always precede 'metadata', 'error', or 'end' callbacks.
 *
 * @param {string} eventType The event type
 * @param {function(?)} callback The callback to handle the event with
 * an optional input object
 * @return {!ClientReadableStream} this object
 */
ClientReadableStream.prototype.on = goog.abstractMethod;



/**
 * Remove a particular callback.
 *
 * @param {string} eventType The event type
 * @param {function(?)} callback The callback to remove
 * @return {!ClientReadableStream} this object
 */
ClientReadableStream.prototype.removeListener = goog.abstractMethod;



/**
 * Close the stream.
 */
ClientReadableStream.prototype.cancel = goog.abstractMethod;



exports = ClientReadableStream;


================================================
FILE: javascript/net/grpc/web/clientunarycallimpl.js
================================================
/**
 * @fileoverview This class handles ClientReadableStream returned by unary
 * calls.
 */

goog.module('grpc.web.ClientUnaryCallImpl');

goog.module.declareLegacyNamespace();

const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');

/**
 * @implements {ClientReadableStream<RESPONSE>}
 * @template RESPONSE
 */
class ClientUnaryCallImpl {
  /**
   * @param {!ClientReadableStream<RESPONSE>} stream
   */
  constructor(stream) {
    this.stream = stream;
  }

  /**
   * @override
   */
  on(eventType, callback) {
    if (eventType == 'data' || eventType == 'error') {
      // unary call responses and errors should be handled by the main
      // (err, resp) => ... callback
      return this;
    }
    return this.stream.on(eventType, callback);
  }

  /**
   * @override
   */
  removeListener(eventType, callback) {
    return this.stream.removeListener(eventType, callback);
  }

  /**
   * @override
   */
  cancel() {
    this.stream.cancel();
  }
}

exports = ClientUnaryCallImpl;


================================================
FILE: javascript/net/grpc/web/generator/BUILD.bazel
================================================
load("@rules_cc//cc:defs.bzl", "cc_binary")

cc_binary(
    name = "protoc-gen-grpc-web",
    srcs = [
        "grpc_generator.cc",
    ],
    visibility = ["//visibility:public"],
    deps = [
        "@com_google_protobuf//:protoc_lib",
    ],
)


================================================
FILE: javascript/net/grpc/web/generator/Makefile
================================================
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

CXX ?= g++
CPPFLAGS += -I/usr/local/include -pthread
CXXFLAGS += -std=c++11
LDFLAGS += -L/usr/local/lib -lprotoc -lprotobuf -lpthread -ldl
PREFIX ?= /usr/local
MIN_MACOS_VERSION := 10.7 # Supports OS X Lion
STATIC ?= yes

UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
  CXXFLAGS += -stdlib=libc++ -mmacosx-version-min=$(MIN_MACOS_VERSION)
else ifeq ($(UNAME_S),Linux)
  ifeq ($(STATIC),yes)
    LDFLAGS += -static
  endif
endif

all: protoc-gen-grpc-web

protoc-gen-grpc-web: grpc_generator.o
	$(CXX) $^ $(LDFLAGS) -o $@

install: protoc-gen-grpc-web
	mkdir -p $(PREFIX)/bin
	install protoc-gen-grpc-web $(PREFIX)/bin/protoc-gen-grpc-web

clean:
	rm -f *.o protoc-gen-grpc-web


================================================
FILE: javascript/net/grpc/web/generator/grpc_generator.cc
================================================
/**
 *
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.h>
#include <google/protobuf/compiler/plugin.pb.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>

#include <algorithm>
#include <iterator>
#include <set>
#include <string>

using google::protobuf::Descriptor;
using google::protobuf::Edition;
using google::protobuf::EnumDescriptor;
using google::protobuf::FieldDescriptor;
using google::protobuf::FileDescriptor;
using google::protobuf::MethodDescriptor;
using google::protobuf::ServiceDescriptor;
using google::protobuf::FieldOptions;
using google::protobuf::OneofDescriptor;
using google::protobuf::compiler::CodeGenerator;
using google::protobuf::compiler::GeneratorContext;
using google::protobuf::compiler::ParseGeneratorParameter;
using google::protobuf::compiler::PluginMain;
using google::protobuf::compiler::Version;
using google::protobuf::io::Printer;
using google::protobuf::io::ZeroCopyOutputStream;

namespace grpc {
namespace web {
namespace {

using std::string;

enum Mode {
  OP = 0,       // first party google3 one platform services
  GRPCWEB = 1,  // client using the application/grpc-web wire format
};

enum ImportStyle {
  CLOSURE = 0,     // goog.require("grpc.web.*")
  COMMONJS = 1,    // const grpcWeb = require("grpc-web")
  TYPESCRIPT = 2,  // import * as grpcWeb from 'grpc-web'
};

const char GRPC_PROMISE[] = "grpc.web.promise.GrpcWebPromise";

const char* kKeyword[] = {
    "abstract",   "boolean",      "break",      "byte",    "case",
    "catch",      "char",         "class",      "const",   "continue",
    "debugger",   "default",      "delete",     "do",      "double",
    "else",       "enum",         "export",     "extends", "false",
    "final",      "finally",      "float",      "for",     "function",
    "goto",       "if",           "implements", "import",  "in",
    "instanceof", "int",          "interface",  "long",    "native",
    "new",        "null",         "package",    "private", "protected",
    "public",     "return",       "short",      "static",  "super",
    "switch",     "synchronized", "this",       "throw",   "throws",
    "transient",  "try",          "typeof",     "var",     "void",
    "volatile",   "while",        "with",
};

// Edit the version here prior to release
static const std::string GRPC_WEB_VERSION = "2.0.2";

string GetProtocVersion(GeneratorContext* context) {
  Version compiler_version;
  context->GetCompilerVersion(&compiler_version);
  return std::to_string(compiler_version.major()) + "." +
         std::to_string(compiler_version.minor()) + "." +
         std::to_string(compiler_version.patch()) +
         compiler_version.suffix();
}

bool IsReserved(const string& ident) {
  for (size_t i = 0; i < sizeof(kKeyword) / sizeof(kKeyword[0]); i++) {
    if (ident == kKeyword[i]) {
      return true;
    }
  }
  return false;
}

string GetModeVar(const Mode mode) {
  switch (mode) {
    case OP:
      return "OP";
    case GRPCWEB:
      return "GrpcWeb";
  }
  return "";
}

string GetDeserializeMethodName(std::map<string, string> vars) {
  if (vars["mode"] == GetModeVar(Mode::OP) && vars["binary"] == "false") {
    return "deserialize";
  }
  return "deserializeBinary";
}

string GetSerializeMethodName(std::map<string, string> vars) {
  if (vars["mode"] == GetModeVar(Mode::OP) && vars["binary"] == "false") {
    return "serialize";
  }
  return "serializeBinary";
}

std::string GetSerializeMethodReturnType(std::map<string, string> vars) {
  if (vars["mode"] == GetModeVar(Mode::OP) && vars["binary"] == "false") {
    return "string";
  }
  return "!Uint8Array";
}

string LowercaseFirstLetter(string s) {
  if (s.empty()) {
    return s;
  }
  s[0] = ::tolower(s[0]);
  return s;
}

string Lowercase(string s) {
  if (s.empty()) {
    return s;
  }

  for (size_t i = 0; i < s.size(); i++) {
    s[i] = ::tolower(s[i]);
  }
  return s;
}

string UppercaseFirstLetter(string s) {
  if (s.empty()) {
    return s;
  }
  s[0] = ::toupper(s[0]);
  return s;
}

string Uppercase(string s) {
  if (s.empty()) {
    return s;
  }

  for (size_t i = 0; i < s.size(); i++) {
    s[i] = ::toupper(s[i]);
  }
  return s;
}

// The following 5 functions were copied from
// google/protobuf/src/google/protobuf/stubs/strutil.h

inline bool HasPrefixString(const string& str, const string& prefix) {
  return str.size() >= prefix.size() &&
         str.compare(0, prefix.size(), prefix) == 0;
}

// Strips the given prefix from the string, as well as the remaining leading dot
// if it exists.
inline string StripPrefixString(const string& str, const string& prefix) {
  if (!HasPrefixString(str, prefix)) {
    return str;
  }

  string remaining_str = str.substr(prefix.size());
  if (!remaining_str.empty() && remaining_str[0] == '.') {
    remaining_str = remaining_str.substr(1);
  }
  return remaining_str;
}

inline bool HasSuffixString(const string& str, const string& suffix) {
  return str.size() >= suffix.size() &&
         str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}

inline string StripSuffixString(const string& str, const string& suffix) {
  if (HasSuffixString(str, suffix)) {
    return str.substr(0, str.size() - suffix.size());
  } else {
    return str;
  }
}

void ReplaceCharacters(string* s, const char* remove, char replacewith) {
  const char* str_start = s->c_str();
  const char* str = str_start;
  for (str = strpbrk(str, remove); str != nullptr;
       str = strpbrk(str + 1, remove)) {
    (*s)[str - str_start] = replacewith;
  }
}

// The following function was copied from
// google/protobuf/src/google/protobuf/compiler/cpp/cpp_helpers.cc

string StripProto(const string& filename) {
  if (HasSuffixString(filename, ".protodevel")) {
    return StripSuffixString(filename, ".protodevel");
  } else {
    return StripSuffixString(filename, ".proto");
  }
}

// The following 6 functions were copied from
// google/protobuf/src/google/protobuf/compiler/js/js_generator.cc

char ToLowerASCII(char c) {
  if (c >= 'A' && c <= 'Z') {
    return (c - 'A') + 'a';
  } else {
    return c;
  }
}

std::vector<string> ParseLowerUnderscore(const string& input) {
  std::vector<string> words;
  string running = "";
  for (size_t i = 0; i < input.size(); i++) {
    if (input[i] == '_') {
      if (!running.empty()) {
        words.push_back(running);
        running.clear();
      }
    } else {
      running += ToLowerASCII(input[i]);
    }
  }
  if (!running.empty()) {
    words.push_back(running);
  }
  return words;
}

string ToUpperCamel(const std::vector<string>& words) {
  string result;
  for (size_t i = 0; i < words.size(); i++) {
    string word = words[i];
    if (word[0] >= 'a' && word[0] <= 'z') {
      word[0] = (word[0] - 'a') + 'A';
    }
    result += word;
  }
  return result;
}

// Returns the alias we assign to the module of the given .proto filename
// when importing.
string ModuleAlias(const string& filename) {
  // This scheme could technically cause problems if a file includes any 2 of:
  //   foo/bar_baz.proto
  //   foo_bar_baz.proto
  //   foo_bar/baz.proto
  //
  // We'll worry about this problem if/when we actually see it.  This name isn't
  // exposed to users so we can change it later if we need to.
  string basename = StripProto(filename);
  ReplaceCharacters(&basename, "-", '$');
  ReplaceCharacters(&basename, "/", '_');
  ReplaceCharacters(&basename, ".", '_');
  return basename + "_pb";
}

string JSMessageType(const Descriptor* desc, const FileDescriptor* file) {
  string class_name = StripPrefixString(desc->full_name(), desc->file()->package());
  if (desc->file() == file) {
    // [for protobuf .d.ts files only] Do not add the module prefix for local
    // messages.
    return class_name;
  }
  return ModuleAlias(desc->file()->name()) + "." + class_name;
}

string JSMessageType(const Descriptor* desc) {
  return JSMessageType(desc, nullptr);
}

string JSElementType(const FieldDescriptor* desc, const FileDescriptor* file) {
  switch (desc->type()) {
    case FieldDescriptor::TYPE_DOUBLE:
    case FieldDescriptor::TYPE_FLOAT:
    case FieldDescriptor::TYPE_INT32:
    case FieldDescriptor::TYPE_UINT32:
    case FieldDescriptor::TYPE_SINT32:
    case FieldDescriptor::TYPE_FIXED32:
    case FieldDescriptor::TYPE_SFIXED32:
      return "number";

    case FieldDescriptor::TYPE_INT64:
    case FieldDescriptor::TYPE_UINT64:
    case FieldDescriptor::TYPE_SINT64:
    case FieldDescriptor::TYPE_FIXED64:
    case FieldDescriptor::TYPE_SFIXED64:
      if (desc->options().jstype() == FieldOptions::JS_STRING) {
        return "string";
      } else {
        return "number";
      }

    case FieldDescriptor::TYPE_BOOL:
      return "boolean";

    case FieldDescriptor::TYPE_STRING:
      return "string";

    case FieldDescriptor::TYPE_BYTES:
      return "Uint8Array | string";

    case FieldDescriptor::TYPE_ENUM:
      if (desc->enum_type()->file() == file) {
        // [for protobuf .d.ts files only] Do not add the module prefix for
        // local messages.
        return StripPrefixString(desc->enum_type()->full_name(),
                                 desc->enum_type()->file()->package());
      }
      return ModuleAlias(desc->enum_type()->file()->name()) + "." +
             StripPrefixString(desc->enum_type()->full_name(),
                               desc->enum_type()->file()->package());

    case FieldDescriptor::TYPE_MESSAGE:
      return JSMessageType(desc->message_type(), file);

    default:
      return "{}";
  }
}

string JSFieldType(const FieldDescriptor* desc, const FileDescriptor* file) {
  string js_field_type = JSElementType(desc, file);
  if (desc->is_map()) {
    string key_type = JSFieldType(desc->message_type()->field(0), file);
    string value_type = JSFieldType(desc->message_type()->field(1), file);
    return "jspb.Map<" + key_type + ", " + value_type + ">";
  }
  if (desc->is_repeated()) {
    return "Array<" + js_field_type + ">";
  }
  return js_field_type;
}

string AsObjectFieldType(const FieldDescriptor* desc,
                         const FileDescriptor* file) {
  if (desc->type() != FieldDescriptor::TYPE_MESSAGE) {
    return JSFieldType(desc, file);
  }
  if (desc->is_map()) {
    const Descriptor* message = desc->message_type();
    string key_type = AsObjectFieldType(message->field(0), file);
    string value_type = AsObjectFieldType(message->field(1), file);
    return "Array<[" + key_type + ", " + value_type + "]>";
  }
  string field_type = JSMessageType(desc->message_type(), file) + ".AsObject";
  if (desc->is_repeated()) {
    return "Array<" + field_type + ">";
  }
  return field_type;
}

string JSElementName(const FieldDescriptor* desc) {
  return ToUpperCamel(ParseLowerUnderscore(desc->name()));
}

string JSFieldName(const FieldDescriptor* desc) {
  string js_field_name = JSElementName(desc);
  if (desc->is_map()) {
    js_field_name += "Map";
  } else if (desc->is_repeated()) {
    js_field_name += "List";
  }
  return js_field_name;
}

// Like ToUpperCamel except the first letter is not converted.
string ToCamelCase(const std::vector<string>& words) {
  if (words.empty()) {
    return "";
  }
  string result = words[0];
  return result + ToUpperCamel(std::vector<string>(
                      words.begin() + 1, words.begin() + words.size()));
}

// Like JSFieldName, but with first letter not uppercased
string CamelCaseJSFieldName(const FieldDescriptor* desc) {
  string js_field_name = ToCamelCase(ParseLowerUnderscore(desc->name()));
  if (desc->is_map()) {
    js_field_name += "Map";
  } else if (desc->is_repeated()) {
    js_field_name += "List";
  }
  return js_field_name;
}

// Returns the name of the message with a leading dot and taking into account
// nesting, for example ".OuterMessage.InnerMessage", or returns empty if
// descriptor is null. This function does not handle namespacing, only message
// nesting.
string GetNestedMessageName(const Descriptor* descriptor) {
  if (descriptor == nullptr) {
    return "";
  }
  string result =
      StripPrefixString(descriptor->full_name(), descriptor->file()->package());
  // Add a leading dot if one is not already present.
  if (!result.empty() && result[0] != '.') {
    result = "." + result;
  }
  return result;
}

// Given a filename like foo/bar/baz.proto, returns the root directory
// path ../../
string GetRootPath(const string& from_filename, const string& to_filename) {
  if (HasPrefixString(to_filename, "google/protobuf")) {
    // Well-known types (.proto files in the google/protobuf directory) are
    // assumed to come from the 'google-protobuf' npm package.  We may want to
    // generalize this exception later by letting others put generated code in
    // their own npm packages.
    return "google-protobuf/";
  }

  size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/');
  if (slashes == 0) {
    return "./";
  }
  string result = "";
  for (size_t i = 0; i < slashes; i++) {
    result += "../";
  }
  return result;
}

// Splits path immediately following the final slash, separating it into a
// directory and file name component. Directory will contain the last
// slash, if it's not empty.
// If there is no slash in path, Split returns an empty directory and
// basename set to path.
// Output values have the property that path = directory + basename.
void PathSplit(const string& path, string* directory, string* basename) {
  string::size_type last_slash = path.rfind('/');
  if (last_slash == string::npos) {
    if (directory) {
      *directory = "";
    }
    if (basename) {
      *basename = path;
    }
  } else {
    if (directory) {
      *directory = path.substr(0, last_slash + 1);
    }
    if (basename) {
      *basename = path.substr(last_slash + 1);
    }
  }
}

// Returns the basename of a file.
string GetBasename(string filename) {
  string basename;
  PathSplit(filename, nullptr, &basename);
  return basename;
}

//Adds $ suffix to reserved method names to avoid conflicts.
static bool IsReservedMethodName(const std::string& name) {
  static const std::unordered_set<std::string> reserved = {
    "extension",
    "jspbmessageid"
  };

  std::string lower_name = Lowercase(name);

  return reserved.count(lower_name) > 0;
}

static std::string SafeAccessorName(const std::string& name) {
  std::string result = name;
  if (IsReservedMethodName(name)) {
    result += "$";
  }
  return result;
}

// Finds all message types used in all services in the file. Return results as a
// map of full names to descriptors to get sorted results and deterministic
// build outputs.
std::map<string, const Descriptor*> GetAllMessages(const FileDescriptor* file) {
  std::map<string, const Descriptor*> messages;
  for (int s = 0; s < file->service_count(); ++s) {
    const ServiceDescriptor* service = file->service(s);
    for (int m = 0; m < service->method_count(); ++m) {
      const MethodDescriptor* method = service->method(m);
      messages[method->input_type()->full_name()] = method->input_type();
      messages[method->output_type()->full_name()] = method->output_type();
    }
  }

  return messages;
}

void PrintClosureDependencies(Printer* printer, const FileDescriptor* file) {
  for (const auto& entry : GetAllMessages(file)) {
    printer->Print("goog.require('proto.$full_name$');\n", "full_name",
                   entry.second->full_name());
  }
}

void PrintCommonJsMessagesDeps(Printer* printer, const FileDescriptor* file) {
  std::map<string, string> vars;

  for (int i = 0; i < file->dependency_count(); i++) {
    const string& name = file->dependency(i)->name();
    vars["alias"] = ModuleAlias(name);
    vars["dep_filename"] = GetRootPath(file->name(), name) + StripProto(name);
    // we need to give each cross-file import an alias
    printer->Print(vars, "\nvar $alias$ = require('$dep_filename$_pb.js')\n");
  }

  const string& package = file->package();
  vars["package_name"] = package;

  if (!package.empty()) {
    size_t offset = 0;
    size_t dotIndex = package.find('.');

    printer->Print(vars, "const proto = {};\n");

    while (dotIndex != string::npos) {
      vars["current_package_ns"] = package.substr(0, dotIndex);
      printer->Print(vars, "proto.$current_package_ns$ = {};\n");

      offset = dotIndex + 1;
      dotIndex = package.find('.', offset);
    }
  }

  // need to import the messages from our own file
  vars["filename"] = GetBasename(StripProto(file->name()));

  if (!package.empty()) {
    printer->Print(vars,
                   "proto.$package_name$ = require('./$filename$_pb.js');\n\n");
  } else {
    printer->Print(vars, "const proto = require('./$filename$_pb.js');\n\n");
  }
}

void PrintES6Imports(Printer* printer, const FileDescriptor* file) {
  std::map<string, string> vars;

  printer->Print("import * as grpcWeb from 'grpc-web';\n\n");

  std::set<string> imports;
  for (const auto& entry : GetAllMessages(file)) {
    const string& proto_filename = entry.second->file()->name();
    string dep_filename = GetRootPath(file->name(), proto_filename) + StripProto(proto_filename);
    if (imports.find(dep_filename) != imports.end()) {
      continue;
    }
    imports.insert(dep_filename);
    // We need to give each cross-file import an alias.
    printer->Print("import * as $alias$ from '$dep_filename$_pb'; // proto import: \"$proto_filename$\"\n",
                   "alias", ModuleAlias(proto_filename),
                   "dep_filename", dep_filename,
                   "proto_filename", proto_filename);
  }
  printer->Print("\n\n");
}

void PrintTypescriptFile(Printer* printer, const FileDescriptor* file,
                         std::map<string, string> vars) {
  PrintES6Imports(printer, file);
  for (int service_index = 0; service_index < file->service_count();
       ++service_index) {
    printer->Print("export class ");
    const ServiceDescriptor* service = file->service(service_index);
    vars["service_name"] = service->name();
    printer->Print(vars, "$service_name$Client {\n");
    printer->Indent();
    printer->Print(
        "client_: grpcWeb.AbstractClientBase;\n"
        "hostname_: string;\n"
        "credentials_: null | { [index: string]: string; };\n"
        "options_: null | { [index: string]: any; };\n\n"
        "constructor (hostname: string,\n"
        "             credentials?: null | { [index: string]: string; },\n"
        "             options?: null | { [index: string]: any; }) {\n");
    printer->Indent();
    printer->Print("if (!options) options = {};\n");
    printer->Print("if (!credentials) credentials = {};\n");
    if (vars["mode"] == GetModeVar(Mode::GRPCWEB)) {
      printer->Print(vars, "options['format'] = '$format$';\n\n");
    }
    printer->Print(vars,
                   "this.client_ = new grpcWeb.$mode$ClientBase(options);\n"
                   "this.hostname_ = hostname.replace(/\\/+$$/, '');\n"
                   "this.credentials_ = credentials;\n"
                   "this.options_ = options;\n");
    printer->Outdent();
    printer->Print("}\n\n");

    for (int method_index = 0; method_index < service->method_count();
         ++method_index) {
      const MethodDescriptor* method = service->method(method_index);
      vars["js_method_name"] = LowercaseFirstLetter(method->name());
      vars["method_name"] = method->name();
      vars["input_type"] = JSMessageType(method->input_type());
      vars["output_type"] = JSMessageType(method->output_type());
      vars["serialize_func_name"] = GetSerializeMethodName(vars);
      vars["deserialize_func_name"] = GetDeserializeMethodName(vars);
      vars["method_type"] = method->server_streaming()
                                ? "grpcWeb.MethodType.SERVER_STREAMING"
                                : "grpcWeb.MethodType.UNARY";
      if (!method->client_streaming()) {
        printer->Print(vars,
                       "methodDescriptor$method_name$ = "
                       "new grpcWeb.MethodDescriptor(\n");
        printer->Indent();
        printer->Print(vars,
                       "'/$package_dot$$service_name$/$method_name$',\n"
                       "$method_type$,\n"
                       "$input_type$,\n"
                       "$output_type$,\n"
                       "(request: $input_type$) => {\n"
                       "  return request.$serialize_func_name$();\n"
                       "},\n"
                       "$output_type$.$deserialize_func_name$\n");
        printer->Outdent();
        printer->Print(");\n\n");
        if (method->server_streaming()) {
          printer->Print(vars, "$js_method_name$(\n");
          printer->Indent();
          printer->Print(vars,
                         "request: $input_type$,\n"
                         "metadata?: grpcWeb.Metadata): "
                         "grpcWeb.ClientReadableStream<$output_type$> {\n");
          printer->Print(vars, "return this.client_.serverStreaming(\n");
          printer->Indent();
          printer->Print(vars,
                         "this.hostname_ +\n"
                         "  '/$package_dot$$service_name$/$method_name$',\n"
                         "request,\n"
                         "metadata || {},\n"
                         "this.methodDescriptor$method_name$);\n");
          printer->Outdent();
          printer->Outdent();
          printer->Print("}\n\n");
        } else {
          printer->Print(vars, "$js_method_name$(\n");
          printer->Indent();
          printer->Print(vars,
                         "request: $input_type$,\n"
                         "metadata?: grpcWeb.Metadata | null): "
                         "$promise$<$output_type$>;\n\n");
          printer->Outdent();

          printer->Print(vars, "$js_method_name$(\n");
          printer->Indent();
          printer->Print(vars,
                         "request: $input_type$,\n"
                         "metadata: grpcWeb.Metadata | null,\n"
                         "callback: (err: grpcWeb.RpcError,\n"
                         "           response: $output_type$) => void): "
                         "grpcWeb.ClientReadableStream<$output_type$>;\n\n");
          printer->Outdent();

          printer->Print(vars, "$js_method_name$(\n");
          printer->Indent();
          printer->Print(vars,
                         "request: $input_type$,\n"
                         "metadata?: grpcWeb.Metadata | null,\n"
                         "callback?: (err: grpcWeb.RpcError,\n"
                         "           response: $output_type$) => void) {\n");
          printer->Print(vars, "if (callback !== undefined) {\n");
          printer->Indent();
          printer->Print(vars, "return this.client_.rpcCall(\n");
          printer->Indent();
          printer->Print(vars,
                         "this.hostname_ +\n"
                         "  '/$package_dot$$service_name$/$method_name$',\n"
                         "request,\n"
                         "metadata || {},\n"
                         "this.methodDescriptor$method_name$,\n"
                         "callback);\n");
          printer->Outdent();
          printer->Outdent();
          printer->Print(vars,
                         "}\n"
                         "return this.client_.unaryCall(\n");
          printer->Print(vars,
                         "this.hostname_ +\n"
                         "  '/$package_dot$$service_name$/$method_name$',\n"
                         "request,\n"
                         "metadata || {},\n"
                         "this.methodDescriptor$method_name$);\n");
          printer->Outdent();
          printer->Print("}\n\n");
        }
      }
    }
    printer->Outdent();
    printer->Print("}\n\n");
  }
}

void PrintGrpcWebDtsClientClass(Printer* printer, const FileDescriptor* file,
                                const string& client_type) {
  std::map<string, string> vars;
  vars["client_type"] = client_type;
  vars["promise"] = "Promise";
  for (int service_index = 0; service_index < file->service_count();
       ++service_index) {
    printer->Print("export class ");
    const ServiceDescriptor* service = file->service(service_index);
    vars["service_name"] = service->name();
    printer->Print(vars, "$service_name$$client_type$ {\n");
    printer->Indent();
    printer->Print(
        "constructor (hostname: string,\n"
        "             credentials?: null | { [index: string]: string; },\n"
        "             options?: null | { [index: string]: any; });\n\n");
    for (int method_index = 0; method_index < service->method_count();
         ++method_index) {
      const MethodDescriptor* method = service->method(method_index);
      vars["js_method_name"] = LowercaseFirstLetter(method->name());
      vars["input_type"] = JSMessageType(method->input_type());
      vars["output_type"] = JSMessageType(method->output_type());
      if (!method->client_streaming()) {
        if (method->server_streaming()) {
          printer->Print(vars, "$js_method_name$(\n");
          printer->Indent();
          printer->Print(vars,
                         "request: $input_type$,\n"
                         "metadata?: grpcWeb.Metadata\n");
          printer->Outdent();
          printer->Print(vars,
                         "): grpcWeb.ClientReadableStream<$output_type$>;\n\n");
        } else {
          if (vars["client_type"] == "PromiseClient") {
            printer->Print(vars, "$js_method_name$(\n");
            printer->Indent();
            printer->Print(vars,
                           "request: $input_type$,\n"
                           "metadata?: grpcWeb.Metadata\n");
            printer->Outdent();
            printer->Print(vars, "): $promise$<$output_type$>;\n\n");
          } else {
            printer->Print(vars, "$js_method_name$(\n");
            printer->Indent();
            printer->Print(vars,
                           "request: $input_type$,\n"
                           "metadata: grpcWeb.Metadata | undefined,\n"
                           "callback: (err: grpcWeb.RpcError,\n"
                           "           response: $output_type$) => void\n");
            printer->Outdent();
            printer->Print(vars,
                           "): grpcWeb.ClientReadableStream<$output_type$>;");
            printer->Print("\n\n");
          }
        }
      }
    }
    printer->Outdent();
    printer->Print("}\n\n");
  }
}

void PrintGrpcWebDtsFile(Printer* printer, const FileDescriptor* file) {
  PrintES6Imports(printer, file);
  PrintGrpcWebDtsClientClass(printer, file, "Client");
  PrintGrpcWebDtsClientClass(printer, file, "PromiseClient");
}

void PrintProtoDtsEnum(Printer* printer, const EnumDescriptor* desc) {
  std::map<string, string> vars;
  vars["enum_name"] = desc->name();

  // Use regular enums for broad TypeScript compatibility. `const enum`
  // triggers TS2748 when `verbatimModuleSyntax` is enabled (default in
  // TypeScript 5.9+), so prefer `enum` here.
  printer->Print(vars, "export enum $enum_name$ {\n");
  printer->Indent();
  for (int i = 0; i < desc->value_count(); i++) {
    vars["value_name"] = Uppercase(desc->value(i)->name());
    vars["value_number"] = std::to_string(desc->value(i)->number());
    printer->Print(vars, "$value_name$ = $value_number$,\n");
  }
  printer->Outdent();
  printer->Print("}\n");
}

void PrintProtoDtsOneofCase(Printer* printer, const OneofDescriptor* desc) {
  std::map<string, string> vars;
  vars["oneof_name"] = ToUpperCamel(ParseLowerUnderscore(desc->name()));
  vars["oneof_name_upper"] = Uppercase(desc->name());

  // Oneof case enums also use regular enums to avoid ambient `const enum`
  // issues under `verbatimModuleSyntax`.
  printer->Print(vars, "export enum $oneof_name$Case {\n");
  printer->Indent();
  printer->Print(vars, "$oneof_name_upper$_NOT_SET = 0,\n");
  for (int i = 0; i < desc->field_count(); i++) {
    const FieldDescriptor* field = desc->field(i);
    vars["field_name"] = Uppercase(field->name());
    vars["field_number"] = std::to_string(field->number());
    printer->Print(vars, "$field_name$ = $field_number$,\n");
  }
  printer->Outdent();
  printer->Print("}\n");
}

void PrintProtoDtsMessage(Printer* printer, const Descriptor* desc,
                          const FileDescriptor* file) {
  const string& class_name = desc->name();
  std::map<string, string> vars;
  vars["class_name"] = class_name;

  printer->Print(vars, "export class $class_name$ extends jspb.Message {\n");
  printer->Indent();

  printer->Print(vars, "constructor();\n" "constructor(opt_data?: $class_name$.AsObject);\n");

  for (int i = 0; i < desc->field_count(); i++) {
    const FieldDescriptor* field = desc->field(i);

    vars["js_field_name"] = SafeAccessorName(JSFieldName(field));
    vars["js_field_type"] = JSFieldType(field, file);
    if (field->type() != FieldDescriptor::TYPE_MESSAGE ||
        field->is_repeated()) {
      printer->Print(vars, "get$js_field_name$(): $js_field_type$;\n");
    } else {
      printer->Print(vars,
                     "get$js_field_name$(): $js_field_type$ | undefined;\n");
    }
    if (field->type() == FieldDescriptor::TYPE_BYTES && !field->is_repeated()) {
      printer->Print(vars,
                     "get$js_field_name$_asU8(): Uint8Array;\n"
                     "get$js_field_name$_asB64(): string;\n");
    }
    if (!field->is_map() && (field->type() != FieldDescriptor::TYPE_MESSAGE ||
                             field->is_repeated())) {
      printer->Print(vars,
                     "set$js_field_name$(value: $js_field_type$): "
                     "$class_name$;\n");
    } else if (!field->is_map()) {
      printer->Print(vars,
                     "set$js_field_name$(value?: $js_field_type$): "
                     "$class_name$;\n");
    }
    if (field->has_presence()) {
      printer->Print(vars, "has$js_field_name$(): boolean;\n");
    }
    if (field->type() == FieldDescriptor::TYPE_MESSAGE ||
        field->has_presence() || field->is_repeated() || field->is_map()) {
      printer->Print(vars, "clear$js_field_name$(): $class_name$;\n");
    }
    if (field->is_repeated() && !field->is_map()) {

      vars["js_field_name"] = SafeAccessorName(JSElementName(field));
      vars["js_field_type"] = JSElementType(field, file);
      if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
        printer->Print(vars,
                       "add$js_field_name$(value: $js_field_type$, "
                       "index?: number): $class_name$;\n");
      } else {
        printer->Print(vars,
                       "add$js_field_name$(value?: $js_field_type$, "
                       "index?: number): $js_field_type$;\n");
      }
    }

    printer->Print("\n");
  }

  for (int i = 0; i < desc->real_oneof_decl_count(); i++) {
    const OneofDescriptor *oneof = desc->real_oneof_decl(i);
    vars["js_oneof_name"] = ToUpperCamel(ParseLowerUnderscore(oneof->name()));
    printer->Print(
        vars, "get$js_oneof_name$Case(): $class_name$.$js_oneof_name$Case;\n");
    printer->Print("\n");
  }

  printer->Print(
      vars,
      "serializeBinary(): Uint8Array;\n"
      "toObject(includeInstance?: boolean): "
      "$class_name$.AsObject;\n"
      "static toObject(includeInstance: boolean, msg: $class_name$): "
      "$class_name$.AsObject;\n"
      "static serializeBinaryToWriter(message: $class_name$, writer: "
      "jspb.BinaryWriter): void;\n"
      "static deserializeBinary(bytes: Uint8Array): $class_name$;\n"
      "static deserializeBinaryFromReader(message: $class_name$, reader: "
      "jspb.BinaryReader): $class_name$;\n");
  printer->Outdent();
  printer->Print("}\n\n");

  printer->Print(vars, "export namespace $class_name$ {\n");
  printer->Indent();
  printer->Print("export type AsObject = {\n");
  printer->Indent();
  for (int i = 0; i < desc->field_count(); i++) {
    const FieldDescriptor* field = desc->field(i);
    
    string js_field_name = CamelCaseJSFieldName(field);
    if (IsReserved(js_field_name)) {
      js_field_name = "pb_" + js_field_name;
    }

    vars["js_field_name"] = js_field_name;
    vars["js_field_type"] = AsObjectFieldType(field, file);
    if (!field->has_presence()) {
      printer->Print(vars, "$js_field_name$: $js_field_type$;\n");
    } else {
      printer->Print(vars, "$js_field_name$?: $js_field_type$;\n");
    }
  }
  printer->Outdent();
  printer->Print("};\n");

  for (int i = 0; i < desc->nested_type_count(); i++) {
    if (desc->nested_type(i)->options().map_entry()) {
      continue;
    }
    printer->Print("\n");
    PrintProtoDtsMessage(printer, desc->nested_type(i), file);
  }

  for (int i = 0; i < desc->enum_type_count(); i++) {
    printer->Print("\n");
    PrintProtoDtsEnum(printer, desc->enum_type(i));
  }

  for (int i = 0; i < desc->oneof_decl_count(); i++) {
    printer->Print("\n");
    PrintProtoDtsOneofCase(printer, desc->oneof_decl(i));
  }

  printer->Outdent();
  printer->Print("}\n\n");
}

void PrintProtoDtsFile(Printer* printer, const FileDescriptor* file) {
  printer->Print("import * as jspb from 'google-protobuf'\n\n");

  for (int i = 0; i < file->dependency_count(); i++) {
    const string& proto_filename = file->dependency(i)->name();
    // We need to give each cross-file import an alias.
    printer->Print("import * as $alias$ from '$dep_filename$_pb'; // proto import: \"$proto_filename$\"\n",
                   "alias", ModuleAlias(proto_filename),
                   "dep_filename", GetRootPath(file->name(), proto_filename) + StripProto(proto_filename),
                   "proto_filename", proto_filename);
  }
  printer->Print("\n\n");

  for (int i = 0; i < file->message_type_count(); i++) {
    PrintProtoDtsMessage(printer, file->message_type(i), file);
  }

  for (int i = 0; i < file->enum_type_count(); i++) {
    PrintProtoDtsEnum(printer, file->enum_type(i));
  }
}

void PrintFileHeader(Printer* printer, const std::map<string, string>& vars) {
  printer->Print(
      vars,
      "/**\n"
      " * @fileoverview gRPC-Web generated client stub for $package$\n"
      " * @enhanceable\n"
      " * @public\n"
      " */\n\n"
      "// Code generated by protoc-gen-grpc-web. DO NOT EDIT.\n"
      "// versions:\n"
      "// \tprotoc-gen-grpc-web v$version$\n"
      "// \tprotoc              v$protoc_version$\n"
      "// source: $source_file$\n\n\n"
      "/* eslint-disable */\n"
      "// @ts-nocheck\n\n\n");
}

void PrintMethodDescriptorFile(Printer* printer,
                               std::map<string, string> vars) {
  printer->Print(
      vars,
      "/**\n"
      " * @fileoverview gRPC-Web generated MethodDescriptors for $package$\n");
  if (vars["plugins"].empty()) {
    printer->Print(" * @enhanceable\n");
  }
  printer->Print(
      " * @public\n"
      " */\n\n"
      "// Code generated by protoc-gen-grpc-web. DO NOT EDIT.\n"
      "// versions:\n"
      "// \tprotoc-gen-grpc-web v$version$\n"
      "// \tprotoc              v$protoc_version$\n"
      "// source: $source_file$\n\n\n"
      "/* eslint-disable */\n"
      "// @ts-nocheck\n\n\n");

  printer->Print(vars,
                 "goog.provide('proto.$package_dot$$class_name$.$"
                 "method_name$MethodDescriptor');\n\n");
  if (!vars["plugins"].empty()) {
    printer->Print(vars,
                   "goog.require('$plugins$.$package_dot$$class_name$.$"
                   "method_name$MethodDescriptor');\n");
  }
  printer->Print(vars, "goog.require('grpc.web.MethodDescriptor');\n");
  printer->Print(vars, "goog.require('grpc.web.MethodType');\n");
  printer->Print(vars, "goog.require('$in_type$');\n");
  if (vars["out_type"] != vars["in_type"]) {
    printer->Print(vars, "goog.require('$out_type$');\n");
  }
  printer->Print(vars, "\n\ngoog.scope(function() {\n\n");

  printer->Print(
      vars,
      "/**\n"
      " * @const\n"
      " * @type {!grpc.web.MethodDescriptor<\n"
      " *   !proto.$in$,\n"
      " *   !proto.$out$>}\n"
      " */\n"
      "proto.$package_dot$$class_name$.$method_name$MethodDescriptor = \n");
  printer->Indent();
  printer->Indent();
  printer->Print(vars, "new grpc.web.MethodDescriptor(\n");
  printer->Indent();
  printer->Indent();
  printer->Print(vars,
                 "'/$package_dot$$service_name$/$method_name$',\n"
                 "$method_type$,\n"
                 "$in_type$,\n");
  printer->Print(vars,
                 "$out_type$,\n"
                 "/**\n"
                 " * @param {!proto.$in$} request\n");
  printer->Print(
      (" * @return {" + GetSerializeMethodReturnType(vars) + "}\n").c_str());
  printer->Print(
      " */\n"
      "function(request) {\n");
  printer->Print(
      ("  return request." + GetSerializeMethodName(vars) + "();\n").c_str());
  printer->Print("},\n");
  printer->Print(vars,
                 ("$out_type$." + GetDeserializeMethodName(vars)).c_str());
  printer->Print(vars, ");\n\n\n");
  printer->Outdent();
  printer->Outdent();
  printer->Outdent();
  printer->Outdent();
  printer->Print("}); // goog.scope\n\n");
}

void PrintServiceConstructor(Printer* printer, std::map<string, string> vars,
                             bool is_promise) {
  vars["is_promise"] = is_promise ? "Promise" : "";
  printer->Print(vars,
                 "/**\n"
                 " * @param {string} hostname\n"
                 " * @param {?Object} credentials\n"
                 " * @param {?grpc.web.ClientOptions} options\n"
                 " * @constructor\n"
                 " * @struct\n"
                 " * @final\n"
                 " */\n"
                 "proto.$package_dot$$service_name$$is_promise$Client =\n"
                 "    function(hostname, credentials, options) {\n"
                 "  if (!options) options = {};\n");
  if (vars["mode"] == GetModeVar(Mode::GRPCWEB)) {
    printer->Print(vars, "  options.format = '$format$';\n\n");
  }
  if (vars["mode"] == GetModeVar(Mode::OP)) {
    printer->Print(
        vars,
        "  /**\n"
        "   * @private @const {!grpc.web.$mode$ClientBase} The client\n"
        "   */\n"
        "  this.client_ = new grpc.web.$mode$ClientBase(options, "
        "$binary$);\n\n");
  } else {
    printer->Print(
        vars,
        "  /**\n"
        "   * @private @const {!grpc.web.$mode$ClientBase} The client\n"
        "   */\n"
        "  this.client_ = new grpc.web.$mode$ClientBase(options);\n\n");
  }
  printer->PrintRaw(
                 "  /**\n"
                 "   * @private @const {string} The hostname\n"
                 "   */\n"
                 "  this.hostname_ = hostname.replace(/\\/+$/, '');\n\n"
                 "};\n\n\n");
}

void PrintMethodDescriptor(Printer* printer, std::map<string, string> vars) {
  printer->Print(vars,
                 "/**\n"
                 " * @const\n"
                 " * @type {!grpc.web.MethodDescriptor<\n"
                 " *   !proto.$in$,\n"
                 " *   !proto.$out$>}\n"
                 " */\n"
                 "const methodDescriptor_$service_name$_$method_name$ = "
                 "new grpc.web.MethodDescriptor(\n");
  printer->Indent();
  printer->Print(vars,
                 "'/$package_dot$$service_name$/$method_name$',\n"
                 "$method_type$,\n"
                 "$in_type$,\n");
  printer->Print(vars,
                 "$out_type$,\n"
                 "/**\n"
                 " * @param {!proto.$in$} request\n");
  printer->Print(
      (" * @return {" + GetSerializeMethodReturnType(vars) + "}\n").c_str());
  printer->Print(
      " */\n"
      "function(request) {\n");
  printer->Print(
      ("  return request." + GetSerializeMethodName(vars) + "();\n").c_str());
  printer->Print("},\n");
  printer->Print(
      vars, ("$out_type$." + GetDeserializeMethodName(vars) + "\n").c_str());
  printer->Outdent();
  printer->Print(vars, ");\n\n\n");
}

void PrintUnaryCall(Printer* printer, std::map<string, string> vars) {
  printer->Print(
      vars,
      "/**\n"
      " * @param {!proto.$in$} request The\n"
      " *     request proto\n"
      " * @param {?Object<string, string>} metadata User defined\n"
      " *     call metadata\n"
      " * @param {function(?grpc.web.RpcError,"
      " ?proto.$out$)}\n"
      " *     callback The callback function(error, response)\n"
      " * @return {!grpc.web.ClientReadableStream<!proto.$out$>|undefined}\n"
      " *     The XHR Node Readable Stream\n"
      " */\n"
      "proto.$package_dot$$service_name$Client.prototype.$js_method_name$ =\n");
  printer->Indent();
  printer->Print(vars,
                 "  function(request, metadata, callback) {\n"
                 "return this.client_.rpcCall(this.hostname_ +\n");
  printer->Indent();
  printer->Indent();
  if (vars["mode"] == GetModeVar(Mode::OP)) {
    printer->Print(vars,
                   "'/$$rpc/$package_dot$$service_name$/$method_name$',\n");
  } else {
    printer->Print(vars, "'/$package_dot$$service_name$/$method_name$',\n");
  }
  printer->Print(vars,
                 "request,\n"
                 "metadata || {},\n"
                 "$method_descriptor$,\n"
                 "callback);\n");
  printer->Outdent();
  printer->Outdent();
  printer->Outdent();
  printer->Print("};\n\n\n");
}

void PrintPromiseUnaryCall(Printer* printer, std::map<string, string> vars) {
  printer->Print(vars,
                 "/**\n"
                 " * @param {!proto.$in$} request The\n"
                 " *     request proto\n"
                 " * @param {?Object<string, string>=} metadata User defined\n"
                 " *     call metadata\n"
                 " * @return {!$promise$<!proto.$out$>}\n"
                 " *     Promise that resolves to the response\n"
                 " */\n"
                 "proto.$package_dot$$service_name$PromiseClient.prototype"
                 ".$js_method_name$ =\n");
  printer->Indent();
  printer->Print(vars,
                 "  function(request, metadata) {\n"
                 "return this.client_.unaryCall(this.hostname_ +\n");
  printer->Indent();
  printer->Indent();
  if (vars["mode"] == GetModeVar(Mode::OP)) {
    printer->Print(vars,
                   "'/$$rpc/$package_dot$$service_name$/$method_name$',\n");
  } else {
    printer->Print(vars, "'/$package_dot$$service_name$/$method_name$',\n");
  }
  printer->Print(vars,
                 "request,\n"
                 "metadata || {},\n"
                 "$method_descriptor$);\n");
  printer->Outdent();
  printer->Outdent();
  printer->Outdent();
  printer->Print("};\n\n\n");
}

void PrintServerStreamingCall(Printer* printer, std::map<string, string> vars) {
  printer->Print(vars,
                 "/**\n"
                 " * @param {!proto.$in$} request The request proto\n"
                 " * @param {?Object<string, string>=} metadata User defined\n"
                 " *     call metadata\n"
                 " * @return {!grpc.web.ClientReadableStream<!proto.$out$>}\n"
                 " *     The XHR Node Readable Stream\n"
                 " */\n"
                 "proto.$package_dot$$service_name$$client_type$.prototype."
                 "$js_method_name$ =\n");
  printer->Indent();
  printer->Print(
      "  function(request, metadata) {\n"
      "return this.client_.serverStreaming(this.hostname_ +\n");
  printer->Indent();
  printer->Indent();
  if (vars["mode"] == GetModeVar(Mode::OP)) {
    printer->Print(vars,
                   "'/$$rpc/$package_dot$$service_name$/$method_name$',\n");
  } else {
    printer->Print(vars, "'/$package_dot$$service_name$/$method_name$',\n");
  }
  printer->Print(vars,
                 "request,\n"
                 "metadata || {},\n"
                 "$method_descriptor$);\n");
  printer->Outdent();
  printer->Outdent();
  printer->Outdent();
  printer->Print("};\n\n\n");
}

void PrintMultipleFilesMode(const FileDescriptor* file, string file_name,
                            GeneratorContext* context,
                            std::map<string, string> vars) {
  std::map<string, string> method_descriptors;
  bool has_server_streaming = false;

  // Print MethodDescriptor files.
  for (int i = 0; i < file->service_count(); ++i) {
    const ServiceDescriptor* service = file->service(i);
    vars["service_name"] = service->name();
    vars["class_name"] = LowercaseFirstLetter(service->name());

    for (int method_index = 0; method_index < service->method_count();
         ++method_index) {
      const MethodDescriptor* method = service->method(method_index);
      string method_file_name = Lowercase(service->name()) + "_" +
                                Lowercase(method->name()) +
                                "_methoddescriptor.js";
      if (method->server_streaming()) {
        has_server_streaming = true;
      }
      std::unique_ptr<ZeroCopyOutputStream> output(
          context->Open(method_file_name));
      Printer printer(output.get(), '$');

      vars["method_name"] = method->name();
      vars["in"] = method->input_type()->full_name();
      vars["in_type"] = "proto." + method->input_type()->full_name();
      vars["out"] = method->output_type()->full_name();
      vars["out_type"] = "proto." + method->output_type()->full_name();
      vars["method_type"] = method->server_streaming()
                                ? "grpc.web.MethodType.SERVER_STREAMING"
                                : "grpc.web.MethodType.UNARY";

      PrintMethodDescriptorFile(&printer, vars);
      method_descriptors[service->name() + "." + method->name()] =
          "proto." + vars["package_dot"] + vars["class_name"] + "." +
          vars["method_name"] + "MethodDescriptor";
    }
  }

  std::unique_ptr<ZeroCopyOutputStream> output1(
      context->Open(file_name + "_client_pb.js"));
  Printer printer1(output1.get(), '$');
  std::unique_ptr<ZeroCopyOutputStream> output2(
      context->Open(file_name + "_promise_client_pb.js"));
  Printer printer2(output2.get(), '$');

  PrintFileHeader(&printer1, vars);
  PrintFileHeader(&printer2, vars);

  // Print the Promise and callback client.
  for (int i = 0; i < file->service_count(); ++i) {
    const ServiceDescriptor* service = file->service(i);
    vars["service_name"] = service->name();
    printer1.Print(vars,
                   "goog.provide('proto.$package_dot$$service_name$"
                   "Client');\n\n");
    printer2.Print(vars,
                   "goog.provide('proto.$package_dot$$service_name$"
                   "PromiseClient');\n\n");
  }

  if (vars["promise"] == GRPC_PROMISE) {
    printer2.Print(vars, "goog.require('grpc.web.promise');\n");
  }
  std::map<string, string>::iterator it;
  for (it = method_descriptors.begin(); it != method_descriptors.end(); it++) {
    vars["import_mtd"] = it->second;
    printer1.Print(vars, "goog.require('$import_mtd$');\n");
    printer2.Print(vars, "goog.require('$import_mtd$');\n");
  }
  printer1.Print(vars, "goog.require('grpc.web.$mode$ClientBase');\n");
  printer1.Print(vars, "goog.require('grpc.web.ClientReadableStream');\n");
  printer1.Print(vars, "goog.require('grpc.web.RpcError');\n");
  printer2.Print(vars, "goog.require('grpc.web.$mode$ClientBase');\n");
  if (has_server_streaming) {
    printer2.Print(vars, "goog.require('grpc.web.ClientReadableStream');\n");
  }

  PrintClosureDependencies(&printer1, file);
  PrintClosureDependencies(&printer2, file);

  printer1.Print(vars, "\ngoog.requireType('grpc.web.ClientOptions');\n");
  printer2.Print(vars, "\ngoog.requireType('grpc.web.ClientOptions');\n");

  printer1.Print("\n\n\n");
  printer2.Print("\n\n\n");
  printer1.Print("goog.scope(function() {\n\n");
  printer2.Print("goog.scope(function() {\n\n");

  for (int service_index = 0; service_index < file->service_count();
       ++service_index) {
    const ServiceDescriptor* service = file->service(service_index);
    vars["service_name"] = service->name();
    PrintServiceConstructor(&printer1, vars, false);
    PrintServiceConstructor(&printer2, vars, true);

    for (int method_index = 0; method_index < service->method_count();
         ++method_index) {
      const MethodDescriptor* method = service->method(method_index);
      const Descriptor* input_type = method->input_type();
      const Descriptor* output_type = method->output_type();
      vars["js_method_name"] = LowercaseFirstLetter(method->name());
      vars["method_name"] = method->name();
      vars["in"] = input_type->full_name();
      vars["out"] = output_type->full_name();
      vars["method_descriptor"] =
          method_descriptors[service->name() + "." + method->name()];
      vars["in_type"] = "proto." + input_type->full_name();
      vars["out_type"] = "proto." + output_type->full_name();

      // Client streaming is not supported yet
      if (!method->client_streaming()) {
        if (method->server_streaming()) {
          vars["method_type"] = "grpc.web.MethodType.SERVER_STREAMING";
          vars["client_type"] = "Client";
          PrintServerStreamingCall(&printer1, vars);
          vars["client_type"] = "PromiseClient";
          PrintServerStreamingCall(&printer2, vars);
        } else {
          vars["method_type"] = "grpc.web.MethodType.UNARY";
          PrintUnaryCall(&printer1, vars);
          PrintPromiseUnaryCall(&printer2, vars);
        }
      }
    }
  }
  printer1.Print("}); // goog.scope\n\n");
  printer2.Print("}); // goog.scope\n\n");
}

void PrintClosureES6Imports(Printer* printer, const FileDescriptor* file,
                            string package_dot) {
  for (int i = 0; i < file->service_count(); ++i) {
    const ServiceDescriptor* service = file->service(i);

    string service_namespace = "proto." + package_dot + service->name();
    printer->Print(
        "import $service_name$Client_import from 'goog:$namespace$';\n",
        "service_name", service->name(), "namespace",
        service_namespace + "Client");
    printer->Print(
        "import $service_name$PromiseClient_import from 'goog:$namespace$';\n",
        "service_name", service->name(), "namespace",
        service_namespace + "PromiseClient");
  }

  printer->Print("\n\n\n");
}

void PrintGrpcWebClosureES6File(Printer* printer, const FileDescriptor* file) {
  string package_dot = file->package().empty() ? "" : file->package() + ".";

  printer->Print(
      "/**\n"
      " * @fileoverview gRPC-Web generated client stub for '$file$'\n"
      " */\n"
      "\n"
      "// Code generated by protoc-gen-grpc-web. DO NOT EDIT.\n"
      "// versions:\n"
      "// \tprotoc-gen-grpc-web v$version$\n"
      "// \tprotoc              v$protoc_version$\n"
      "// source: $source_file$\n"
      "\n"
      "\n",
      "file", file->name());

  PrintClosureES6Imports(printer, file, package_dot);

  for (int i = 0; i < file->service_count(); ++i) {
    const ServiceDescriptor* service = file->service(i);

    string service_namespace = "proto." + package_dot + service->name();
    printer->Print("export const $name$Client = $name$Client_import;\n", "name",
                   service->name());
    printer->Print(
        "export const $name$PromiseClient = $name$PromiseClient_import;\n",
        "name", service->name());
  }
}

class GeneratorOptions {
 public:
  GeneratorOptions();

  bool ParseFromOptions(const string& parameter, string* error);
  bool ParseFromOptions(const std::vector<std::pair<string, string>>& options,
                        string* error);

  // Returns the name of the output file for |proto_file|.
  string OutputFile(const string& proto_file) const;

  string mode() const { return mode_; }
  string plugins() const { return plugins_; }
  ImportStyle import_style() const { return import_style_; }
  bool generate_dts() const { return generate_dts_; }
  bool generate_closure_es6() const { return generate_closure_es6_; }
  bool multiple_files() const { return multiple_files_; }
  bool goog_promise() const { return goog_promise_; }

 private:
  string file_name_;
  string mode_;
  string plugins_;
  ImportStyle import_style_;
  bool generate_dts_;
  bool generate_closure_es6_;
  bool multiple_files_;
  bool goog_promise_;
};

GeneratorOptions::GeneratorOptions()
    : file_name_(""),
      mode_(""),
      plugins_(""),
      import_style_(ImportStyle::CLOSURE),
      generate_dts_(false),
      generate_closure_es6_(false),
      multiple_files_(false),
      goog_promise_(false) {}

bool GeneratorOptions::ParseFromOptions(const string& parameter,
                                        string* error) {
  std::vector<std::pair<string, string>> options;
  ParseGeneratorParameter(parameter, &options);
  return ParseFromOptions(options, error);
}

bool GeneratorOptions::ParseFromOptions(
    const std::vector<std::pair<string, string>>& options, string* error) {
  for (const std::pair<string, string>& option : options) {
    if ("out" == option.first) {
      file_name_ = option.second;
    } else if ("mode" == option.first) {
      mode_ = option.second;
    } else if ("import_style" == option.first) {
      if ("closure" == option.second) {
        import_style_ = ImportStyle::CLOSURE;
      } else if ("experimental_closure_es6" == option.second) {
        import_style_ = ImportStyle::CLOSURE;
        generate_closure_es6_ = true;
      } else if ("commonjs" == option.second) {
        import_style_ = ImportStyle::COMMONJS;
      } else if ("commonjs+dts" == option.second) {
        import_style_ = ImportStyle::COMMONJS;
        generate_dts_ = true;
      } else if ("typescript" == option.second) {
        import_style_ = ImportStyle::TYPESCRIPT;
        generate_dts_ = true;
      } else {
        *error = "options: invalid import_style - " + option.second;
        return false;
      }
    } else if ("multiple_files" == option.first) {
      multiple_files_ = "True" == option.second;
    } else if ("plugins" == option.first) {
      plugins_ = option.second;
    } else if ("goog_promise" == option.first) {
      goog_promise_ = "True" == option.second;
    } else {
      *error = "unsupported option: " + option.first;
      return false;
    }
  }

  if (mode_.empty()) {
    *error = "options: mode is required";
    return false;
  }

  return true;
}

string GeneratorOptions::OutputFile(const string& proto_file) const {
  if (ImportStyle::TYPESCRIPT == import_style()) {
    // Never use the value from the 'out' option when generating TypeScript.
    string directory;
    string basename;
    PathSplit(proto_file, &directory, &basename);
    return directory + UppercaseFirstLetter(StripProto(basename)) +
           "ServiceClientPb.ts";
  }
  if (!file_name_.empty()) {
    return file_name_;
  }
  return StripProto(proto_file) + "_grpc_web_pb.js";
}

class GrpcCodeGenerator : public CodeGenerator {
 public:
  GrpcCodeGenerator() {}
  ~GrpcCodeGenerator() override {}

  uint64_t GetSupportedFeatures() const override {
    // Code generators must explicitly support proto3 optional.
    return CodeGenerator::FEATURE_PROTO3_OPTIONAL | CodeGenerator::FEATURE_SUPPORTS_EDITIONS;
  }

  // Keep synced with protoc-gen-js: https://github.com/protocolbuffers/protobuf-javascript/blob/861c8020a5c0cba9b7cdf915dffde96a4421a1f4/generator/js_generator.h#L157-L158
  Edition GetMinimumEdition() const override { return Edition::EDITION_PROTO2; }
  Edition GetMaximumEdition() const override { return Edition::EDITION_2023; }

  bool Generate(const FileDescriptor* file, const string& parameter,
                GeneratorContext* context, string* error) const override {
    GeneratorOptions generator_options;
    if (!generator_options.ParseFromOptions(parameter, error)) {
      return false;
    }

    std::map<string, string> vars;
    std::map<string, string> method_descriptors;
    string package = file->package();
    vars["package"] = package;
    vars["package_dot"] = package.empty() ? "" : package + '.';
    vars["promise"] = "Promise";
    vars["plugins"] = generator_options.plugins();

    if ("binary" == generator_options.mode()) {
      vars["mode"] = GetModeVar(Mode::OP);
      vars["binary"] = "true";
    } else if ("grpcweb" == generator_options.mode()) {
      vars["mode"] = GetModeVar(Mode::GRPCWEB);
      vars["format"] = "binary";
    } else if ("grpcwebtext" == generator_options.mode()) {
      vars["mode"] = GetModeVar(Mode::GRPCWEB);
      vars["format"] = "text";
    } else if ("jspb" == generator_options.mode()) {
      vars["mode"] = GetModeVar(Mode::OP);
      vars["binary"] = "false";
      if (generator_options.goog_promise()) {
        vars["promise"] = GRPC_PROMISE;
      }
    } else {
      *error = "options: invalid mode - " + generator_options.mode();
      return false;
    }

    if (generator_options.generate_dts()) {
      string proto_dts_file_name = StripProto(file->name()) + "_pb.d.ts";
      std::unique_ptr<ZeroCopyOutputStream> proto_dts_output(
          context->Open(proto_dts_file_name));
      Printer proto_dts_printer(proto_dts_output.get(), '$');
      PrintProtoDtsFile(&proto_dts_printer, file);
    }

    if (!file->service_count()) {
      // No services, nothing to do.
      return true;
    }

    vars["version"]        = GRPC_WEB_VERSION;
    vars["protoc_version"] = GetProtocVersion(context);
    vars["source_file"]    = file->name();

    string file_name = generator_options.OutputFile(file->name());
    if (generator_options.multiple_files() &&
        ImportStyle::CLOSURE == generator_options.import_style()) {
      PrintMultipleFilesMode(file, file_name, context, vars);
      return true;
    }

    std::unique_ptr<ZeroCopyOutputStream> output(context->Open(file_name));
    Printer printer(output.get(), '$');
    PrintFileHeader(&printer, vars);

    if (ImportStyle::TYPESCRIPT == generator_options.import_style()) {
      PrintTypescriptFile(&printer, file, vars);
      return true;
    }

    for (int i = 0; i < file->service_count(); ++i) {
      const ServiceDescriptor* service = file->service(i);
      vars["service_name"] = service->name();
      switch (generator_options.import_style()) {
        case ImportStyle::CLOSURE:
          printer.Print(
              vars,
              "goog.provide('proto.$package_dot$$service_name$Client');\n");
          printer.Print(vars,
                        "goog.provide('proto.$package_dot$$service_name$"
                        "PromiseClient');\n");
          break;
        case ImportStyle::COMMONJS:
          break;
        case ImportStyle::TYPESCRIPT:
          break;
      }
    }
    printer.Print("\n");

    switch (generator_options.import_style()) {
      case ImportStyle::CLOSURE:
        if (vars["promise"] == GRPC_PROMISE) {
          printer.Print(vars, "goog.require('grpc.web.promise');\n");
        }
        printer.Print(vars, "goog.require('grpc.web.MethodDescriptor');\n");
        printer.Print(vars, "goog.require('grpc.web.MethodType');\n");
        printer.Print(vars, "goog.require('grpc.web.$mode$ClientBase');\n");
        printer.Print(vars, "goog.require('grpc.web.AbstractClientBase');\n");
        printer.Print(vars, "goog.require('grpc.web.ClientReadableStream');\n");
        printer.Print(vars, "goog.require('grpc.web.RpcError');\n");

        PrintClosureDependencies(&printer, file);
        printer.Print(vars, "\ngoog.requireType('grpc.web.ClientOptions');\n");
        printer.Print("\n\n\n");
        printer.Print("goog.scope(function() {\n\n");
        break;
      case ImportStyle::COMMONJS:
        printer.Print(vars, "const grpc = {};\n");
        printer.Print(vars, "grpc.web = require('grpc-web');\n\n");
        PrintCommonJsMessagesDeps(&printer, file);
        break;
      case ImportStyle::TYPESCRIPT:
        break;
    }

    for (int service_index = 0; service_index < file->service_count();
         ++service_index) {
      const ServiceDescriptor* service = file->service(service_index);
      vars["service_name"] = service->name();
      PrintServiceConstructor(&printer, vars, false);
      PrintServiceConstructor(&printer, vars, true);

      for (int method_index = 0; method_index < service->method_count();
           ++method_index) {
        const MethodDescriptor* method = service->method(method_index);
        const Descriptor* input_type = method->input_type();
        const Descriptor* output_type = method->output_type();
        vars["js_method_name"] = LowercaseFirstLetter(method->name());
        vars["method_name"] = method->name();
        vars["in"] = input_type->full_name();
        vars["out"] = output_type->full_name();
        vars["method_descriptor"] =
            "methodDescriptor_" + service->name() + "_" + method->name();

        // Cross-file ref in CommonJS needs to use the module alias instead
        // of the global name.
        if (ImportStyle::COMMONJS == generator_options.import_style() &&
            input_type->file() != file) {
          vars["in_type"] = ModuleAlias(input_type->file()->name()) +
                            GetNestedMessageName(input_type);
        } else {
          vars["in_type"] = "proto." + input_type->full_name();
        }
        if (ImportStyle::COMMONJS == generator_options.import_style() &&
            output_type->file() != file) {
          vars["out_type"] = ModuleAlias(output_type->file()->name()) +
                             GetNestedMessageName(output_type);
        } else {
          vars["out_type"] = "proto." + output_type->full_name();
        }

        // Client streaming is not supported yet
        if (!method->client_streaming()) {
          if (method->server_streaming()) {
            vars["method_type"] = "grpc.web.MethodType.SERVER_STREAMING";
            PrintMethodDescriptor(&printer, vars);
            vars["client_type"] = "Client";
            PrintServerStreamingCall(&printer, vars);
            vars["client_type"] = "PromiseClient";
            PrintServerStreamingCall(&printer, vars);
          } else {
            vars["method_type"] = "grpc.web.MethodType.UNARY";
            PrintMethodDescriptor(&printer, vars);
            PrintUnaryCall(&printer, vars);
            PrintPromiseUnaryCall(&printer, vars);
          }
        }
      }
    }

    switch (generator_options.import_style()) {
      case ImportStyle::CLOSURE:
        printer.Print("}); // goog.scope\n\n");
        break;
      case ImportStyle::COMMONJS:
        if (!vars["package"].empty()) {
          printer.Print(vars, "module.exports = proto.$package$;\n\n");
        } else {
          printer.Print(vars, "module.exports = proto;\n\n");
        }
        break;
      case ImportStyle::TYPESCRIPT:
        break;
    }

    if (generator_options.generate_dts()) {
      string grpcweb_dts_file_name =
          StripProto(file->name()) + "_grpc_web_pb.d.ts";
      string proto_dts_file_name = StripProto(file->name()) + "_pb.d.ts";

      std::unique_ptr<ZeroCopyOutputStream> grpcweb_dts_output(
          context->Open(grpcweb_dts_file_name));
      Printer grpcweb_dts_printer(grpcweb_dts_output.get(), '$');

      PrintGrpcWebDtsFile(&grpcweb_dts_printer, file);
    }

    if (generator_options.generate_closure_es6()) {
      string es6_file_name = StripProto(file->name()) + ".pb.grpc-web.js";

      std::unique_ptr<ZeroCopyOutputStream> es6_output(
          context->Open(es6_file_name));
      Printer es6_printer(es6_output.get(), '$');

      PrintGrpcWebClosureES6File(&es6_printer, file);
    }

    return true;
  }
};

}  // namespace
}  // namespace web
}  // namespace grpc

int main(int argc, char* argv[]) {
  if (argc == 2 && std::string(argv[1]) == "--version") {
    std::cout << argv[0] << " " << grpc::web::GRPC_WEB_VERSION << std::endl;
    return 0;
  }

  grpc::web::GrpcCodeGenerator generator;
  PluginMain(argc, argv, &generator);
  return 0;
}


================================================
FILE: javascript/net/grpc/web/generictransportinterface.js
================================================
/**
 *
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
/**
 * @fileoverview gRPC-Web generic transport interface
 *
 * This class provides an abstraction for the underlying transport
 * implementation underneath the ClientReadableStream layer.
 *
 * @author stanleycheung@google.com (Stanley Cheung)
 */
goog.module('grpc.web.GenericTransportInterface');

goog.module.declareLegacyNamespace();


const NodeReadableStream = goog.require('goog.net.streams.NodeReadableStream');
const XhrIo = goog.require('goog.net.XhrIo');


/**
 * @typedef {{
 *   nodeReadableStream: (?NodeReadableStream|undefined),
 *   xhr: (?XhrIo|undefined),
 * }}
 */
exports.GenericTransportInterface;


================================================
FILE: javascript/net/grpc/web/grpcwebclientbase.js
================================================
/**
 *
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
/**
 * @fileoverview gRPC browser client library.
 *
 * Base class for gRPC Web JS clients using the application/grpc-web wire
 * format
 *
 * @author stanleycheung@google.com (Stanley Cheung)
 */
goog.module('grpc.web.GrpcWebClientBase');

goog.module.declareLegacyNamespace();


const ClientOptions = goog.requireType('grpc.web.ClientOptions');
const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const ClientUnaryCallImpl = goog.require('grpc.web.ClientUnaryCallImpl');
const GrpcWebClientReadableStream = goog.require('grpc.web.GrpcWebClientReadableStream');
const HttpCors = goog.require('goog.net.rpc.HttpCors');
const MethodDescriptor = goog.requireType('grpc.web.MethodDescriptor');
const Request = goog.require('grpc.web.Request');
const RpcError = goog.require('grpc.web.RpcError');
const StatusCode = goog.require('grpc.web.StatusCode');
const XhrIo = goog.require('goog.net.XhrIo');
const googCrypt = goog.require('goog.crypt.base64');
const {AbstractClientBase, PromiseCallOptions, getHostname} = goog.require('grpc.web.AbstractClientBase');
const {Status} = goog.require('grpc.web.Status');
const {StreamInterceptor, UnaryInterceptor} = goog.require('grpc.web.Interceptor');
const {toObject} = goog.require('goog.collections.maps');



/**
 * Base class for gRPC web client using the application/grpc-web wire format
 * @implements {AbstractClientBase}
 * @unrestricted
 */
class GrpcWebClientBase {
  /**
   * @param {!ClientOptions=} options
   * @param {!XhrIo=} xhrIo
   */
  constructor(options = {}, xhrIo = undefined) {
    /**
     * @const
     * @private {string}
     */
    this.format_ =
        options.format || goog.getObjectByName('format', options) || 'text';

    /**
     * @const
     * @private {boolean}
     */
    this.suppressCorsPreflight_ = options.suppressCorsPreflight ||
        goog.getObjectByName('suppressCorsPreflight', options) || false;

    /**
     * @const
     * @private {boolean}
     */
    this.withCredentials_ = options.withCredentials ||
        goog.getObjectByName('withCredentials', options) || false;

    /**
     * @const {!Array<!StreamInterceptor>}
     * @private
     */
    this.streamInterceptors_ = options.streamInterceptors ||
        goog.getObjectByName('streamInterceptors', options) || [];

    /**
     * @const {!Array<!UnaryInterceptor>}
     * @private
     */
    this.unaryInterceptors_ = options.unaryInterceptors ||
        goog.getObjectByName('unaryInterceptors', options) || [];

    /** @const @private {?XhrIo} */
    this.xhrIo_ = xhrIo || null;
  }

  /**
   * @override
   * @export
   */
  rpcCall(method, requestMessage, metadata, methodDescriptor, callback) {
    const hostname = getHostname(method, methodDescriptor);
    const invoker = GrpcWebClientBase.runInterceptors_(
        (request) => this.startStream_(request, hostname),
        this.streamInterceptors_);
    const stream = /** @type {!ClientReadableStream<?>} */ (invoker.call(
        this, methodDescriptor.createRequest(requestMessage, metadata)));
    GrpcWebClientBase.setCallback_(stream, callback, false);
    return new ClientUnaryCallImpl(stream);
  }

  /**
   * @param {string} method The method to invoke
   * @param {REQUEST} requestMessage The request proto
   * @param {!Object<string, string>} metadata User defined call metadata
   * @param {!MethodDescriptor<REQUEST, RESPONSE>} methodDescriptor
   * @param {?PromiseCallOptions=} options Options for the call
   * @return {!Promise<RESPONSE>}
   * @template REQUEST, RESPONSE
   */
  thenableCall(
      method, requestMessage, metadata, methodDescriptor, options = {}) {
    const hostname = getHostname(method, methodDescriptor);
    const signal = options && options.signal;
    const initialInvoker = (request) => new Promise((resolve, reject) => {
      // If the signal is already aborted, immediately reject the promise
      // and don't issue the call.
      if (signal && signal.aborted) {
        const error = new RpcError(StatusCode.CANCELLED, 'Aborted');
        error.cause = signal.reason;
        reject(error);
        return;
      }

      const stream = this.startStream_(request, hostname);
      let unaryMetadata;
      let unaryStatus;
      let unaryMsg;
      GrpcWebClientBase.setCallback_(
          stream,
          (error, response, status, metadata, unaryResponseReceived) => {
            if (error) {
              reject(error);
            } else if (unaryResponseReceived) {
              unaryMsg = response;
            } else if (status) {
              unaryStatus = status;
            } else if (metadata) {
              unaryMetadata = metadata;
            } else {
              resolve(request.getMethodDescriptor().createUnaryResponse(
                  unaryMsg, unaryMetadata, unaryStatus));
            }
          },
          true);

      // Wire up cancellation from the abort signal, if any.
      if (signal) {
        signal.addEventListener('abort', () => {
          stream.cancel();

          const error = new RpcError(StatusCode.CANCELLED, 'Aborted');
          error.cause = /** @type {!AbortSignal} */ (signal).reason;
          reject(error);
        });
      }
    });
    const invoker = GrpcWebClientBase.runInterceptors_(
        initialInvoker, this.unaryInterceptors_);
    const unaryResponse = /** @type {!Promise<?>} */ (invoker.call(
        this, methodDescriptor.createRequest(requestMessage, metadata)));
    return unaryResponse.then((response) => response.getResponseMessage());
  }

  /**
   * @export
   * @param {string} method The method to invoke
   * @param {REQUEST} requestMessage The request proto
   * @param {!Object<string, string>} metadata User defined call metadata
   * @param {!MethodDescriptor<REQUEST, RESPONSE>} methodDescriptor Information
   *     of this RPC method
   * @param {?PromiseCallOptions=} options Options for the call
   * @return {!Promise<RESPONSE>}
   * @template REQUEST, RESPONSE
   */
  unaryCall(method, requestMessage, metadata, methodDescriptor, options = {}) {
    return /** @type {!Promise<RESPONSE>}*/ (this.thenableCall(
        method, requestMessage, metadata, methodDescriptor, options));
  }

  /**
   * @override
   * @export
   */
  serverStreaming(method, requestMessage, metadata, methodDescriptor) {
    const hostname = getHostname(method, methodDescriptor);
    const invoker = GrpcWebClientBase.runInterceptors_(
        (request) => this.startStream_(request, hostname),
        this.streamInterceptors_);
    return /** @type {!ClientReadableStream<?>} */ (invoker.call(
        this, methodDescriptor.createRequest(requestMessage, metadata)));
  }

  /**
   * @private
   * @template REQUEST, RESPONSE
   * @param {!Request<REQUEST, RESPONSE>} request
   * @param {string} hostname
   * @return {!ClientReadableStream<RESPONSE>}
   */
  startStream_(request, hostname) {
    const methodDescriptor = request.getMethodDescriptor();
    let path = hostname + methodDescriptor.getName();

    const xhr = this.xhrIo_ ? this.xhrIo_ : new XhrIo();
    xhr.setWithCredentials(this.withCredentials_);

    const genericTransportInterface = {
      xhr: xhr,
    };
    const stream = new GrpcWebClientReadableStream(genericTransportInterface);
    stream.setResponseDeserializeFn(
        methodDescriptor.getResponseDeserializeFn());

    const metadata = request.getMetadata();
    for(const key in metadata) {
      xhr.headers.set(key, metadata[key]);
    }
    this.processHeaders_(xhr);
    if (this.suppressCorsPreflight_) {
      const headerObject = toObject(xhr.headers);
      xhr.headers.clear();
      path = GrpcWebClientBase.setCorsOverride_(path, headerObject);
    }

    const requestSerializeFn = methodDescriptor.getRequestSerializeFn();
    const serialized = requestSerializeFn(request.getRequestMessage());
    let payload = this.encodeRequest_(serialized);
    if (this.format_ == 'text') {
      payload = googCrypt.encodeByteArray(payload);
    } else if (this.format_ == 'binary') {
      xhr.setResponseType(XhrIo.ResponseType.ARRAY_BUFFER);
    }
    xhr.send(path, 'POST', payload);
    return stream;
  }

  /**
   * @private
   * @static
   * @template RESPONSE
   * @param {!ClientReadableStream<RESPONSE>} stream
   * @param {function(?RpcError, ?RESPONSE, ?Status=, ?Object<string, string>=, ?boolean)|
   *     function(?RpcError,?RESPONSE)} callback
   * @param {boolean} useUnaryResponse Pass true to have the client make
   * multiple calls to the callback, using (error, response, status,
   * metadata, unaryResponseReceived) arguments. One of error, status,
   * metadata, or unaryResponseReceived will be truthy to indicate which piece
   * of information the client is providing in that call. After the stream
   * ends, it will call the callback an additional time with all falsy
   * arguments. Pass false to have the client make one call to the callback
   * using (error, response) arguments.
   */
  static setCallback_(stream, callback, useUnaryResponse) {
    let isResponseReceived = false;
    let responseReceived = null;
    let errorEmitted = false;

    stream.on('data', function(response) {
      isResponseReceived = true;
      responseReceived = response;
    });

    stream.on('error', function(error) {
      if (error.code != StatusCode.OK && !errorEmitted) {
        errorEmitted = true;
        callback(error, null);
      }
    });

    stream.on('status', function(status) {
      if (status.code != StatusCode.OK && !errorEmitted) {
        errorEmitted = true;
        callback(
            {
              code: status.code,
              message: status.details,
              metadata: status.metadata
            },
            null);
      } else if (useUnaryResponse) {
        callback(null, null, status);
      }
    });

    if (useUnaryResponse) {
      stream.on('metadata', function(metadata) {
        callback(null, null, null, metadata);
      });
    }

    stream.on('end', function() {
      if (!errorEmitted) {
        if (!isResponseReceived) {
          callback({
            code: StatusCode.UNKNOWN,
            message: 'Incomplete response',
          });
        } else if (useUnaryResponse) {
          callback(
            null, responseReceived, null, null,
            /* unaryResponseReceived= */ true);
        } else {
          callback(null, responseReceived);
        }
      }
      if (useUnaryResponse) {
        callback(null, null);
      }
    });
  }

  /**
   * Encode the grpc-web request
   *
   * @private
   * @param {!Uint8Array} serialized The serialized proto payload
   * @return {!Uint8Array} The application/grpc-web padded request
   */
  encodeRequest_(serialized) {
    let len = serialized.length;
    const bytesArray = [0, 0, 0, 0];
    const payload = new Uint8Array(5 + len);
    for (let i = 3; i >= 0; i--) {
      bytesArray[i] = (len % 256);
      len = len >>> 8;
    }
    payload.set(new Uint8Array(bytesArray), 1);
    payload.set(serialized, 5);
    return payload;
  }

  /**
   * @private
   * @param {!XhrIo} xhr The xhr object
   */
  processHeaders_(xhr) {
    if (this.format_ == 'text') {
      xhr.headers.set('Content-Type', 'application/grpc-web-text');
      xhr.headers.set('Accept', 'application/grpc-web-text');
    } else {
      xhr.headers.set('Content-Type', 'application/grpc-web+proto');
    }
    xhr.headers.set('X-User-Agent', 'grpc-web-javascript/0.1');
    xhr.headers.set('X-Grpc-Web', '1');
    if (xhr.headers.has('deadline')) {
      const deadline = Number(xhr.headers.get('deadline'));  // in ms
      const currentTime = (new Date()).getTime();
      let timeout = Math.ceil(deadline - currentTime);
      xhr.headers.delete('deadline');
      if (timeout === Infinity) {
        // grpc-timeout header defaults to infinity if not set.
        timeout = 0;
      }
      if (timeout > 0) {
        xhr.headers.set('grpc-timeout', timeout + 'm');
        // Also set timeout on the xhr request to terminate the HTTP request
        // if the server doesn't respond within the deadline. We use 110% of
        // grpc-timeout for this to allow the server to terminate the connection
        // with DEADLINE_EXCEEDED rather than terminating it in the Browser, but
        // at least 1 second in case the user is on a high-latency network.
        xhr.setTimeoutInterval(Math.max(1000, Math.ceil(timeout * 1.1)));
      }
    }
  }

  /**
   * @private
   * @static
   * @param {string} method The method to invoke
   * @param {!Object<string,string>} headerObject The xhr headers
   * @return {string} The URI object or a string path with headers
   */
  static setCorsOverride_(method, headerObject) {
    return /** @type {string} */ (HttpCors.setHttpHeadersWithOverwriteParam(
        method, HttpCors.HTTP_HEADERS_PARAM_NAME, headerObject));
  }

  /**
   * @private
   * @static
   * @template REQUEST, RESPONSE
   * @param {function(!Request<REQUEST,RESPONSE>):
   *     (!Promise<RESPONSE>|!ClientReadableStream<RESPONSE>)} invoker
   * @param {!Array<!UnaryInterceptor|!StreamInterceptor>}
   *     interceptors
   * @return {function(!Request<REQUEST,RESPONSE>):
   *     (!Promise<RESPONSE>|!ClientReadableStream<RESPONSE>)}
   */
  static runInterceptors_(invoker, interceptors) {
    return interceptors.reduce((accumulatedInvoker, interceptor) => {
      return (request) => interceptor.intercept(request, accumulatedInvoker);
    }, invoker);
  }
}



exports = GrpcWebClientBase;


================================================
FILE: javascript/net/grpc/web/grpcwebclientbase_test.js
================================================
/**
 *
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
goog.module('grpc.web.GrpcWebClientBaseTest');
goog.setTestOnly('grpc.web.GrpcWebClientBaseTest');

const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const ErrorCode = goog.require('goog.net.ErrorCode');
const GrpcWebClientBase = goog.require('grpc.web.GrpcWebClientBase');
const MethodDescriptor = goog.require('grpc.web.MethodDescriptor');
const ReadyState = goog.require('goog.net.XmlHttp.ReadyState');
const Request = goog.requireType('grpc.web.Request');
const RpcError = goog.require('grpc.web.RpcError');
const StatusCode = goog.require('grpc.web.StatusCode');
const XhrIo = goog.require('goog.testing.net.XhrIo');
const googCrypt = goog.require('goog.crypt.base64');
const testSuite = goog.require('goog.testing.testSuite');
const {StreamInterceptor} = goog.require('grpc.web.Interceptor');
goog.require('goog.testing.jsunit');

// This parses to [ { DATA: [4, 5, 6] }, { TRAILER: "a: b" } ]
const DEFAULT_RPC_RESPONSE =
    new Uint8Array([0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98]);
const DEFAULT_RPC_RESPONSE_DATA = [4, 5, 6];
const DEFAULT_UNARY_HEADERS =
    ['Content-Type', 'Accept', 'X-User-Agent', 'X-Grpc-Web'];
const DEFAULT_UNARY_HEADER_VALUES = [
  'application/grpc-web-text',
  'application/grpc-web-text',
  'grpc-web-javascript/0.1',
  '1',
];
const DEFAULT_RESPONSE_HEADERS = {
  'Content-Type': 'application/grpc-web-text',
};

testSuite({
  async testRpcResponse() {
    const xhr = new XhrIo();
    const client = new GrpcWebClientBase(/* options= */ {}, xhr);
    const methodDescriptor = createMethodDescriptor((bytes) => {
      assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
      return new MockReply('value');
    });

    const response = await new Promise((resolve, reject) => {
      client.rpcCall(
          'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
          (error, response) => {
            assertNull(error);
            resolve(response);
          });
      xhr.simulatePartialResponse(
          googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
          DEFAULT_RESPONSE_HEADERS);
      xhr.simulateReadyStateChange(ReadyState.COMPLETE);
    });

    assertEquals('value', response.data);
    const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
    assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
    assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
  },

  async testRpcFalsyResponse_ForNonProtobufDescriptor() {
    const xhr = new XhrIo();
    const client = new GrpcWebClientBase(/* options= */ {}, xhr);
    const methodDescriptor = createMethodDescriptor((bytes) => {
      assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
      return 0;
    });

    const response = await new Promise((resolve, reject) => {
      client.rpcCall(
          'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
          (error, response) => {
            assertNull(error);
            resolve(response);
          });
      xhr.simulatePartialResponse(
          googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
          DEFAULT_RESPONSE_HEADERS);
      xhr.simulateReadyStateChange(ReadyState.COMPLETE);
    });

    assertEquals(0, response);
    const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
    assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
    assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
  },

  async testRpcResponseThenableCall() {
    const xhr = new XhrIo();
    const client = new GrpcWebClientBase(/* options= */ {}, xhr);
    const methodDescriptor = createMethodDescriptor((bytes) => {
      assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
      return new MockReply('value');
    });

    const responsePromise = client.thenableCall(
      'url', new MockRequest(), /* metadata= */ {}, methodDescriptor);
    xhr.simulatePartialResponse(
        googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
        DEFAULT_RESPONSE_HEADERS);
    xhr.simulateReadyStateChange(ReadyState.COMPLETE);
    const response = await responsePromise;

    assertEquals('value', /** @type {!MockReply} */ (response).data);
    const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
    assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
    assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
  },

  async testRpcFalsyResponseThenableCall_ForNonProtobufDescriptor() {
    const xhr = new XhrIo();
    const client = new GrpcWebClientBase(/* options= */ {}, xhr);
    const methodDescriptor = createMethodDescriptor((bytes) => {
      assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
      return 0;
    });

    const responsePromise = client.thenableCall(
      'url', new MockRequest(), /* metadata= */ {}, methodDescriptor);
    xhr.simulatePartialResponse(
        googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
        DEFAULT_RESPONSE_HEADERS);
    xhr.simulateReadyStateChange(ReadyState.COMPLETE);
    const response = await responsePromise;

    assertEquals(0, response);
    const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
    assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
    assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
  },


  async testCancelledThenableCall() {
    const xhr = new XhrIo();
    const client = new GrpcWebClientBase(/* options= */ {}, xhr);
    const methodDescriptor = createMethodDescriptor((bytes) => {
      assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
      return 0;
    });

    const abortController = new AbortController();
    const signal = abortController.signal;
    const responsePromise = client.thenableCall(
        'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
        {signal});
    abortController.abort();

    const error = await assertRejects(responsePromise);
    assertTrue(error instanceof RpcError);
    assertEquals(StatusCode.CANCELLED, /** @type {!RpcError} */ (error).code);
    assertEquals('Aborted', /** @type {!RpcError} */ (error).message);
    // Default abort reason if none provided.
    const cause = /** @type {!RpcError} */ (error).cause;
    assertTrue(cause instanceof Error);
    assertEquals('AbortError', /** @type {!Error} */ (cause).name);
    assertEquals(ErrorCode.ABORT, xhr.getLastErrorCode());
  },

  async testCancelledThenableCallWithReason() {
    const xhr = new XhrIo();
    const client = new GrpcWebClientBase(/* options= */ {}, xhr);
    const methodDescriptor = createMethodDescriptor((bytes) => {
      assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
      return 0;
    });

    const abortController = new AbortController();
    const signal = abortController.signal;
    const responsePromise = client.thenableCall(
        'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
        {signal});
    abortController.abort('cancelling');

    const error = await assertRejects(responsePromise);
    assertTrue(error instanceof RpcError);
    assertEquals(StatusCode.CANCELLED, /** @type {!RpcError} */ (error).code);
    assertEquals('Aborted', /** @type {!RpcError} */ (error).message);
    // Abort reason forwarded as cause.
    const cause = /** @type {!RpcError} */ (error).cause;
    assertEquals('cancelling', cause);
    assertEquals(ErrorCode.ABORT, xhr.getLastErrorCode());
  },

  async testDeadline() {
    const xhr = new XhrIo();
    const client = new GrpcWebClientBase(/* options= */ {}, xhr);
    const methodDescriptor = createMethodDescriptor((bytes) => new MockReply());

    const deadline = new Date();
    deadline.setSeconds(deadline.getSeconds() + 1);
    await new Promise((resolve, reject) => {
      client.rpcCall(
          'url', new MockRequest(), {'deadline': deadline.getTime().toString()},
          methodDescriptor, (error, response) => {
            assertNull(error);
            resolve();
          });
      xhr.simulatePartialResponse(
          googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
          DEFAULT_RESPONSE_HEADERS);
      xhr.simulateReadyStateChange(ReadyState.COMPLETE);
    });
    const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
    const headersWithDeadline = [...DEFAULT_UNARY_HEADERS, 'grpc-timeout'];
    assertElementsEquals(headersWithDeadline, Object.keys(headers));
  },

  async testRpcError() {
    const xhr = new XhrIo();
    const client = new GrpcWebClientBase(/* options= */ {}, xhr);
    const methodDescriptor = createMethodDescriptor((bytes) => new MockReply());

    const error = await new Promise((resolve, reject) => {
      client.rpcCall(
          'urlurl', new MockRequest(), /* metadata= */ {}, methodDescriptor,
          (error, response) => {
            assertNull(response);
            resolve(error);
          });
      // This decodes to "grpc-status: 3"
      xhr.simulatePartialResponse(
          googCrypt.encodeByteArray(new Uint8Array([
            128, 0,   0,  0,   14,  103, 114, 112, 99, 45,
            115, 116, 97, 116, 117, 115, 58,  32,  51,
          ])),
          DEFAULT_RESPONSE_HEADERS);
    });
    assertTrue(error instanceof RpcError);
    assertEquals(3, error.code);
  },

  async testRpcDeserializationError() {
    const xhr = new XhrIo();
    const client = new GrpcWebClientBase(/* options= */ {}, xhr);

    const responseDeserializeFn = () => {
      throw new Error('Decoding error :)');
    };
    const methodDescriptor = createMethodDescriptor(responseDeserializeFn);
    const error = await new Promise((resolve, reject) => {
      client.rpcCall(
          'urlurl', new MockRequest(), /* metadata= */ {}, methodDescriptor,
          (error, response) => {
            assertNull(response);
            resolve(error);
          });
      xhr.simulatePartialResponse(
          googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
          DEFAULT_RESPONSE_HEADERS);
    });
    assertTrue(error instanceof RpcError);
    assertEquals(StatusCode.INTERNAL, error.code);
  },

  async testRpcResponseHeader() {
    const xhr = new XhrIo();
    const client = new GrpcWebClientBase(/* options= */ {}, xhr);
    const methodDescriptor = createMethodDescriptor((bytes) => {
      assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
      return new MockReply('value');
    });

    const metadata = await new Promise((resolve, reject) => {
      const call = client.rpcCall(
          'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
          (error, response) => {
            assertNull(error);
          });
      call.on('metadata', (metadata) => {
        resolve(metadata);
      });
      xhr.simulatePartialResponse(
          googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)), {
            'Content-Type': 'application/grpc-web-text',
            'initial-metadata-key': 'initial-metadata-value',
          });
      xhr.simulateReadyStateChange(ReadyState.COMPLETE);
    });
    assertEquals('initial-metadata-value', metadata['initial-metadata-key']);
  },

  async testStreamInterceptor() {
    const xhr = new XhrIo();
    const interceptor = new StreamResponseInterceptor();
    const methodDescriptor = createMethodDescriptor((bytes) => {
      assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
      return new MockReply('value');
    });
    const client =
        new GrpcWebClientBase({'streamInterceptors': [interceptor]}, xhr);

    const response = await new Promise((resolve, reject) => {
      client.rpcCall(
          'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
          (error, response) => {
            assertNull(error);
            resolve(response);
          });
      xhr.simulatePartialResponse(
          googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
          DEFAULT_RESPONSE_HEADERS);
      xhr.simulateReadyStateChange(ReadyState.COMPLETE);
    });
    assertEquals('Intercepted value', response.data);
  },

});

/** Mocks a request proto object. */
class MockRequest {
  /**
   * @param {string=} data
   */
  constructor(data = '') {
    /** @type {string} */
    this.data = data;
  }
}

/** Mocks a response proto object. */
class MockReply {
  /**
   * @param {string=} data
   */
  constructor(data = '') {
    /** @type {string} */
    this.data = data;
  }
}

/**
 * Typedef for allowed response types.
 *
 * Number is allowed specifically for supporting falsy responses `0`, see:
 * https://github.com/grpc/grpc-web/pull/1025
 *
 * @typedef {!MockReply|number}
 */
let AllowedResponseType;

/**
 * @param {function(string): !AllowedResponseType} responseDeSerializeFn
 * @return {!MethodDescriptor<!MockRequest, !AllowedResponseType>}
 */
function createMethodDescriptor(responseDeSerializeFn) {
  return new MethodDescriptor(
      /* name= */ '', /* methodType= */ null, MockRequest, MockReply,
      (request) => [1, 2, 3], responseDeSerializeFn);
}


/**
 * @implements {StreamInterceptor}
 * @unrestricted
 */
class StreamResponseInterceptor {
  constructor() {}

  /**
   * @override
   * @template REQUEST, RESPONSE
   * @param {!Request<REQUEST, RESPONSE>} request
   * @param {function(!Request<REQUEST,RESPONSE>):
   *     !ClientReadableStream<RESPONSE>} invoker
   * @return {!ClientReadableStream<RESPONSE>}
   */
  intercept(request, invoker) {
    return new InterceptedStream(invoker(request));
  }
}

/**
 * @implements {ClientReadableStream}
 * @template RESPONSE
 * @final
 */
class InterceptedStream {
  /**
   * @param {!ClientReadableStream<RESPONSE>} stream
   */
  constructor(stream) {
    /** @const {!ClientReadableStream<RESPONSE>} */
    this.stream = stream;
  }

  /**
   * @override
   * @param {string} eventType
   * @param {function(?)} callback
   * @return {!ClientReadableStream<RESPONSE>}
   */
  on(eventType, callback) {
    if (eventType == 'data') {
      const newCallback = (response) => {
        response.data = 'Intercepted ' + response.data;
        callback(response);
      };
      this.stream.on(eventType, newCallback);
    } else {
      this.stream.on(eventType, callback);
    }
    return this;
  }

  /**
   * @override
   * @return {!ClientReadableStream<RESPONSE>}
   */
  cancel() {
    this.stream.cancel();
    return this;
  }

  /**
   * @override
   * @param {string} eventType
   * @param {function(?)} callback
   * @return {!ClientReadableStream<RESPONSE>}
   */
  removeListener(eventType, callback) {
    this.stream.removeListener(eventType, callback);
    return this;
  }
}


================================================
FILE: javascript/net/grpc/web/grpcwebclientreadablestream.js
================================================
/**
 *
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
/**
 * @fileoverview gRPC web client Readable Stream
 *
 * This class is being returned after a gRPC streaming call has been
 * started. This class provides functionality for user to operates on
 * the stream, e.g. set onData callback, etc.
 *
 * This wraps the underlying goog.net.streams.NodeReadableStream
 *
 * @author stanleycheung@google.com (Stanley Cheung)
 */
goog.module('grpc.web.GrpcWebClientReadableStream');

goog.module.declareLegacyNamespace();


const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const ErrorCode = goog.require('goog.net.ErrorCode');
const EventType = goog.require('goog.net.EventType');
const GrpcWebStreamParser = goog.require('grpc.web.GrpcWebStreamParser');
const RpcError = goog.require('grpc.web.RpcError');
const StatusCode = goog.require('grpc.web.StatusCode');
const XhrIo = goog.require('goog.net.XhrIo');
const events = goog.require('goog.events');
const googCrypt = goog.require('goog.crypt.base64');
const googString = goog.require('goog.string');
const {GenericTransportInterface} = goog.require('grpc.web.GenericTransportInterface');
const {Status} = goog.require('grpc.web.Status');



const GRPC_STATUS = 'grpc-status';
const GRPC_STATUS_MESSAGE = 'grpc-message';

/** @type {!Array<string>} */
const EXCLUDED_RESPONSE_HEADERS =
    ['content-type', GRPC_STATUS, GRPC_STATUS_MESSAGE];

/**
 * A stream that the client can read from. Used for calls that are streaming
 * from the server side.
 * @template RESPONSE
 * @implements {ClientReadableStream}
 * @final
 * @unrestricted
 */
class GrpcWebClientReadableStream {
  /**
   * @param {!GenericTransportInterface} genericTransportInterface The
   *   GenericTransportInterface
   */
  constructor(genericTransportInterface) {
    /**
     * @const
     * @private
     * @type {?XhrIo} The XhrIo object
     */
    this.xhr_ = /** @type {?XhrIo} */ (genericTransportInterface.xhr);

    /**
     * @private
     * @type {function(?):!RESPONSE|null} The deserialize function for the proto
     */
    this.responseDeserializeFn_ = null;

    /**
     * @const
     * @private
     * @type {!Array<function(!RESPONSE)>} The list of data callbacks
     */
    this.onDataCallbacks_ = [];

    /**
     * @const
     * @private
     * @type {!Array<function(!Status)>} The list of status callbacks
     */
    this.onStatusCallbacks_ = [];

    /**
     * @const
     * @private
     * @type {!Array<function(!Metadata)>} The list of metadata callbacks
     */
    this.onMetadataCallbacks_ = [];

    /**
     * @const
     * @private
     * @type {!Array<function(!RpcError)>} The list of error callbacks
     */
    this.onErrorCallbacks_ = [];

    /**
     * @const
     * @private
     * @type {!Array<function(...):?>} The list of stream end callbacks
     */
    this.onEndCallbacks_ = [];

    /**
     * @private
     * @type {boolean} Whether the stream has been aborted
     */
    this.aborted_ = false;

    /**
     * @private
     * @type {number} The stream parser position
     */
    this.pos_ = 0;

    /**
     * @private
     * @type {!GrpcWebStreamParser} The grpc-web stream parser
     * @const
     */
    this.parser_ = new GrpcWebStreamParser();

    const self = this;
    events.listen(this.xhr_, EventType.READY_STATE_CHANGE, function(e) {
      let contentType = self.xhr_.getStreamingResponseHeader('Content-Type');
      if (!contentType) return;
      contentType = contentType.toLowerCase();

      let byteSource;
      if (googString.startsWith(contentType, 'application/grpc-web-text')) {
        // Ensure responseText is not null
        const responseText = self.xhr_.getResponseText() || '';
        const newPos = responseText.length - responseText.length % 4;
        const newData = responseText.substr(self.pos_, newPos - self.pos_);
        if (newData.length == 0) return;
        self.pos_ = newPos;
        byteSource = googCrypt.decodeStringToUint8Array(newData);
      } else if (googString.startsWith(contentType, 'application/grpc')) {
        byteSource = new Uint8Array(
            /** @type {!ArrayBuffer} */ (self.xhr_.getResponse()));
      } else {
        self.handleError_(
            new RpcError(StatusCode.UNKNOWN, 'Unknown Content-type received.'));
        return;
      }
      let messages = null;
      try {
        messages = self.
Download .txt
gitextract_uo_g1mbd/

├── .bazelci/
│   └── presubmit.yml
├── .bazelignore
├── .bazelrc
├── .bazelrc.windows
├── .dockerignore
├── .github/
│   └── workflows/
│       ├── make-plugin-linux.yml
│       ├── make-plugin-mac-os.yml
│       ├── make-plugin-windows.yml
│       └── release-source-archive.yml
├── .gitignore
├── .gitmodules
├── AUTHORS
├── CHANGELOG.md
├── CODE-OF-CONDUCT.md
├── CONTRIBUTING.md
├── GOVERNANCE.md
├── LICENSE
├── MAINTAINERS.md
├── MODULE.bazel
├── Makefile
├── PATENTS
├── README.md
├── SECURITY.md
├── doc/
│   ├── browser-features.md
│   ├── in-process-proxy.md
│   ├── interop-test-descriptions.md
│   ├── roadmap.md
│   └── streaming-roadmap.md
├── docker-compose.yml
├── etc/
│   ├── localhost.crt
│   └── localhost.key
├── javascript/
│   └── net/
│       └── grpc/
│           └── web/
│               ├── abstractclientbase.js
│               ├── calloptions.js
│               ├── clientoptions.js
│               ├── clientreadablestream.js
│               ├── clientunarycallimpl.js
│               ├── generator/
│               │   ├── BUILD.bazel
│               │   ├── Makefile
│               │   └── grpc_generator.cc
│               ├── generictransportinterface.js
│               ├── grpcwebclientbase.js
│               ├── grpcwebclientbase_test.js
│               ├── grpcwebclientreadablestream.js
│               ├── grpcwebstreamparser.js
│               ├── grpcwebstreamparser_test.js
│               ├── interceptor.js
│               ├── metadata.js
│               ├── methoddescriptor.js
│               ├── methoddescriptorinterface.js
│               ├── methodtype.js
│               ├── request.js
│               ├── requestinternal.js
│               ├── rpcerror.js
│               ├── status.js
│               ├── statuscode.js
│               ├── statuscode_test.js
│               ├── unaryresponse.js
│               └── unaryresponseinternal.js
├── kokoro/
│   ├── interop.cfg
│   ├── master.cfg
│   └── presubmit.cfg
├── net/
│   └── grpc/
│       └── gateway/
│           ├── docker/
│           │   ├── binary_client/
│           │   │   └── Dockerfile
│           │   ├── closure_client/
│           │   │   └── Dockerfile
│           │   ├── commonjs_client/
│           │   │   └── Dockerfile
│           │   ├── echo_server/
│           │   │   └── Dockerfile
│           │   ├── envoy/
│           │   │   └── Dockerfile
│           │   ├── grpcwebproxy/
│           │   │   └── Dockerfile
│           │   ├── interop_client/
│           │   │   └── Dockerfile
│           │   ├── node_interop_server/
│           │   │   └── Dockerfile
│           │   ├── node_server/
│           │   │   └── Dockerfile
│           │   ├── prereqs/
│           │   │   └── Dockerfile
│           │   └── ts_client/
│           │       └── Dockerfile
│           └── examples/
│               ├── echo/
│               │   ├── .gitignore
│               │   ├── BUILD.bazel
│               │   ├── Makefile
│               │   ├── README.md
│               │   ├── commonjs-example/
│               │   │   ├── .gitignore
│               │   │   ├── client.js
│               │   │   ├── echotest.html
│               │   │   ├── package.json
│               │   │   └── webpack.config.js
│               │   ├── echo.proto
│               │   ├── echo_chat.js
│               │   ├── echo_server.cc
│               │   ├── echo_service_impl.cc
│               │   ├── echo_service_impl.h
│               │   ├── echoapp.js
│               │   ├── echotest.html
│               │   ├── envoy.yaml
│               │   ├── node-server/
│               │   │   ├── .gitignore
│               │   │   ├── package.json
│               │   │   └── server.js
│               │   ├── package.json
│               │   ├── ts-example/
│               │   │   ├── .gitignore
│               │   │   ├── README.md
│               │   │   ├── client.ts
│               │   │   ├── echotest.html
│               │   │   ├── package.json
│               │   │   ├── tsconfig.json
│               │   │   └── webpack.config.js
│               │   └── tutorial.md
│               └── helloworld/
│                   ├── .gitignore
│                   ├── README.md
│                   ├── client.js
│                   ├── debugging/
│                   │   └── node-client.js
│                   ├── envoy.yaml
│                   ├── helloworld.proto
│                   ├── index.html
│                   ├── package.json
│                   └── server.js
├── packages/
│   └── grpc-web/
│       ├── .gitignore
│       ├── .npmignore
│       ├── README.md
│       ├── docker/
│       │   └── jsunit-test/
│       │       └── Dockerfile
│       ├── exports.js
│       ├── externs.js
│       ├── gulpfile.js
│       ├── index.d.ts
│       ├── package.json
│       ├── protractor.conf.js
│       ├── protractor_spec.js
│       ├── scripts/
│       │   ├── build.js
│       │   ├── common.py
│       │   ├── gen_all_tests_js.py
│       │   ├── gen_test_htmls.py
│       │   ├── generate_test_files.sh
│       │   ├── run_jsunit_tests.sh
│       │   ├── template_all_tests_js.txt
│       │   └── template_test_html.txt
│       └── test/
│           ├── closure_client.js
│           ├── common.js
│           ├── eval_test.js
│           ├── export_test.js
│           ├── generated_code_test.js
│           ├── gulpfile.js
│           ├── plugin_test.js
│           ├── protos/
│           │   ├── echo.proto
│           │   ├── foo.proto
│           │   ├── models.proto
│           │   ├── myapi/
│           │   │   └── v1/
│           │   │       ├── myapi-two.proto
│           │   │       └── myapi.proto
│           │   ├── nopackage.proto
│           │   ├── otherapi/
│           │   │   └── v1/
│           │   │       └── otherapi.proto
│           │   ├── test01.proto
│           │   ├── test02.proto
│           │   └── test03.proto
│           ├── tsc-tests/
│           │   ├── client01.ts
│           │   ├── client02.ts
│           │   ├── client03.ts
│           │   ├── client04.ts
│           │   ├── client05.ts
│           │   └── client06.ts
│           └── tsc_test.js
├── scripts/
│   ├── README.md
│   ├── docker-run-build-tests.sh
│   ├── docker-run-interop-tests.sh
│   ├── docker-run-jsunit-tests.sh
│   ├── docker-run-mocha-tests.sh
│   ├── init_submodules.sh
│   ├── kokoro.sh
│   ├── release_notes.py
│   ├── run_basic_tests.sh
│   ├── run_interop_tests.sh
│   └── test-proxy.sh
├── src/
│   └── proto/
│       └── grpc/
│           └── testing/
│               ├── empty.proto
│               ├── messages.proto
│               └── test.proto
└── test/
    └── interop/
        ├── .gitignore
        ├── README.md
        ├── envoy.yaml
        ├── index.html
        ├── interop_client.js
        ├── package.json
        └── webpack.config.js
Download .txt
SYMBOL INDEX (292 symbols across 37 files)

FILE: javascript/net/grpc/web/abstractclientbase.js
  method constructor (line 54) | constructor() {}
  method rpcCall (line 68) | rpcCall(method, requestMessage, metadata, methodDescriptor, callback) {}
  method thenableCall (line 83) | thenableCall(method, requestMessage, metadata, methodDescriptor, options...
  method serverStreaming (line 95) | serverStreaming(method, requestMessage, metadata, methodDescriptor) {}
  function getHostname (line 105) | function getHostname(method, methodDescriptor) {

FILE: javascript/net/grpc/web/calloptions.js
  class CallOptions (line 12) | class CallOptions {
    method constructor (line 16) | constructor(options) {
    method setOption (line 32) | setOption(name, value) {
    method get (line 43) | get(name) {
    method removeOption (line 52) | removeOption(name) {
    method getKeys (line 59) | getKeys() {

FILE: javascript/net/grpc/web/clientoptions.js
  class ClientOptions (line 11) | class ClientOptions {
    method constructor (line 12) | constructor() {

FILE: javascript/net/grpc/web/clientunarycallimpl.js
  class ClientUnaryCallImpl (line 16) | class ClientUnaryCallImpl {
    method constructor (line 20) | constructor(stream) {
    method on (line 27) | on(eventType, callback) {
    method removeListener (line 39) | removeListener(eventType, callback) {
    method cancel (line 46) | cancel() {

FILE: javascript/net/grpc/web/generator/grpc_generator.cc
  type grpc (line 49) | namespace grpc {
    type web (line 50) | namespace web {
      type Mode (line 55) | enum Mode {
      type ImportStyle (line 60) | enum ImportStyle {
      function string (line 86) | string GetProtocVersion(GeneratorContext* context) {
      function IsReserved (line 95) | bool IsReserved(const string& ident) {
      function string (line 104) | string GetModeVar(const Mode mode) {
      function string (line 114) | string GetDeserializeMethodName(std::map<string, string> vars) {
      function string (line 121) | string GetSerializeMethodName(std::map<string, string> vars) {
      function GetSerializeMethodReturnType (line 128) | std::string GetSerializeMethodReturnType(std::map<string, string> va...
      function string (line 135) | string LowercaseFirstLetter(string s) {
      function string (line 143) | string Lowercase(string s) {
      function string (line 154) | string UppercaseFirstLetter(string s) {
      function string (line 162) | string Uppercase(string s) {
      function HasPrefixString (line 176) | inline bool HasPrefixString(const string& str, const string& prefix) {
      function string (line 183) | inline string StripPrefixString(const string& str, const string& pre...
      function HasSuffixString (line 195) | inline bool HasSuffixString(const string& str, const string& suffix) {
      function string (line 200) | inline string StripSuffixString(const string& str, const string& suf...
      function ReplaceCharacters (line 208) | void ReplaceCharacters(string* s, const char* remove, char replacewi...
      function string (line 220) | string StripProto(const string& filename) {
      function ToLowerASCII (line 231) | char ToLowerASCII(char c) {
      function ParseLowerUnderscore (line 239) | std::vector<string> ParseLowerUnderscore(const string& input) {
      function string (line 258) | string ToUpperCamel(const std::vector<string>& words) {
      function string (line 272) | string ModuleAlias(const string& filename) {
      function string (line 287) | string JSMessageType(const Descriptor* desc, const FileDescriptor* f...
      function string (line 297) | string JSMessageType(const Descriptor* desc) {
      function string (line 301) | string JSElementType(const FieldDescriptor* desc, const FileDescript...
      function string (line 351) | string JSFieldType(const FieldDescriptor* desc, const FileDescriptor...
      function string (line 364) | string AsObjectFieldType(const FieldDescriptor* desc,
      function string (line 382) | string JSElementName(const FieldDescriptor* desc) {
      function string (line 386) | string JSFieldName(const FieldDescriptor* desc) {
      function string (line 397) | string ToCamelCase(const std::vector<string>& words) {
      function string (line 407) | string CamelCaseJSFieldName(const FieldDescriptor* desc) {
      function string (line 421) | string GetNestedMessageName(const Descriptor* descriptor) {
      function string (line 436) | string GetRootPath(const string& from_filename, const string& to_fil...
      function PathSplit (line 462) | void PathSplit(const string& path, string* directory, string* basena...
      function string (line 482) | string GetBasename(string filename) {
      function IsReservedMethodName (line 489) | static bool IsReservedMethodName(const std::string& name) {
      function SafeAccessorName (line 500) | static std::string SafeAccessorName(const std::string& name) {
      function GetAllMessages (line 511) | std::map<string, const Descriptor*> GetAllMessages(const FileDescrip...
      function PrintClosureDependencies (line 525) | void PrintClosureDependencies(Printer* printer, const FileDescriptor...
      function PrintCommonJsMessagesDeps (line 532) | void PrintCommonJsMessagesDeps(Printer* printer, const FileDescripto...
      function PrintES6Imports (line 572) | void PrintES6Imports(Printer* printer, const FileDescriptor* file) {
      function PrintTypescriptFile (line 594) | void PrintTypescriptFile(Printer* printer, const FileDescriptor* file,
      function PrintGrpcWebDtsClientClass (line 730) | void PrintGrpcWebDtsClientClass(Printer* printer, const FileDescript...
      function PrintGrpcWebDtsFile (line 792) | void PrintGrpcWebDtsFile(Printer* printer, const FileDescriptor* fil...
      function PrintProtoDtsEnum (line 798) | void PrintProtoDtsEnum(Printer* printer, const EnumDescriptor* desc) {
      function PrintProtoDtsOneofCase (line 816) | void PrintProtoDtsOneofCase(Printer* printer, const OneofDescriptor*...
      function PrintProtoDtsMessage (line 836) | void PrintProtoDtsMessage(Printer* printer, const Descriptor* desc,
      function PrintProtoDtsFile (line 967) | void PrintProtoDtsFile(Printer* printer, const FileDescriptor* file) {
      function PrintFileHeader (line 989) | void PrintFileHeader(Printer* printer, const std::map<string, string...
      function PrintMethodDescriptorFile (line 1006) | void PrintMethodDescriptorFile(Printer* printer,
      function PrintServiceConstructor (line 1082) | void PrintServiceConstructor(Printer* printer, std::map<string, stri...
      function PrintMethodDescriptor (line 1124) | void PrintMethodDescriptor(Printer* printer, std::map<string, string...
      function PrintUnaryCall (line 1157) | void PrintUnaryCall(Printer* printer, std::map<string, string> vars) {
      function PrintPromiseUnaryCall (line 1195) | void PrintPromiseUnaryCall(Printer* printer, std::map<string, string...
      function PrintServerStreamingCall (line 1229) | void PrintServerStreamingCall(Printer* printer, std::map<string, str...
      function PrintMultipleFilesMode (line 1262) | void PrintMultipleFilesMode(const FileDescriptor* file, string file_...
      function PrintClosureES6Imports (line 1394) | void PrintClosureES6Imports(Printer* printer, const FileDescriptor* ...
      function PrintGrpcWebClosureES6File (line 1413) | void PrintGrpcWebClosureES6File(Printer* printer, const FileDescript...
      class GeneratorOptions (line 1444) | class GeneratorOptions {
        method string (line 1455) | string mode() const { return mode_; }
        method string (line 1456) | string plugins() const { return plugins_; }
        method ImportStyle (line 1457) | ImportStyle import_style() const { return import_style_; }
        method generate_dts (line 1458) | bool generate_dts() const { return generate_dts_; }
        method generate_closure_es6 (line 1459) | bool generate_closure_es6() const { return generate_closure_es6_; }
        method multiple_files (line 1460) | bool multiple_files() const { return multiple_files_; }
        method goog_promise (line 1461) | bool goog_promise() const { return goog_promise_; }
      function string (line 1536) | string GeneratorOptions::OutputFile(const string& proto_file) const {
      class GrpcCodeGenerator (line 1551) | class GrpcCodeGenerator : public CodeGenerator {
        method GrpcCodeGenerator (line 1553) | GrpcCodeGenerator() {}
        method GetSupportedFeatures (line 1556) | uint64_t GetSupportedFeatures() const override {
        method Edition (line 1562) | Edition GetMinimumEdition() const override { return Edition::EDITI...
        method Edition (line 1563) | Edition GetMaximumEdition() const override { return Edition::EDITI...
        method Generate (line 1565) | bool Generate(const FileDescriptor* file, const string& parameter,
  function main (line 1779) | int main(int argc, char* argv[]) {

FILE: javascript/net/grpc/web/grpcwebclientbase.js
  class GrpcWebClientBase (line 54) | class GrpcWebClientBase {
    method constructor (line 59) | constructor(options = {}, xhrIo = undefined) {
    method rpcCall (line 103) | rpcCall(method, requestMessage, metadata, methodDescriptor, callback) {
    method thenableCall (line 123) | thenableCall(
    method unaryCall (line 188) | unaryCall(method, requestMessage, metadata, methodDescriptor, options ...
    method serverStreaming (line 197) | serverStreaming(method, requestMessage, metadata, methodDescriptor) {
    method startStream_ (line 213) | startStream_(request, hostname) {
    method setCallback_ (line 266) | static setCallback_(stream, callback, useUnaryResponse) {
    method encodeRequest_ (line 332) | encodeRequest_(serialized) {
    method processHeaders_ (line 349) | processHeaders_(xhr) {
    method setCorsOverride_ (line 386) | static setCorsOverride_(method, headerObject) {
    method runInterceptors_ (line 402) | static runInterceptors_(invoker, interceptors) {

FILE: javascript/net/grpc/web/grpcwebclientbase_test.js
  constant DEFAULT_RPC_RESPONSE (line 36) | const DEFAULT_RPC_RESPONSE =
  constant DEFAULT_RPC_RESPONSE_DATA (line 38) | const DEFAULT_RPC_RESPONSE_DATA = [4, 5, 6];
  constant DEFAULT_UNARY_HEADERS (line 39) | const DEFAULT_UNARY_HEADERS =
  constant DEFAULT_UNARY_HEADER_VALUES (line 41) | const DEFAULT_UNARY_HEADER_VALUES = [
  constant DEFAULT_RESPONSE_HEADERS (line 47) | const DEFAULT_RESPONSE_HEADERS = {
  method testRpcResponse (line 52) | async testRpcResponse() {
  method testRpcFalsyResponse_ForNonProtobufDescriptor (line 79) | async testRpcFalsyResponse_ForNonProtobufDescriptor() {
  method testRpcResponseThenableCall (line 106) | async testRpcResponseThenableCall() {
  method testRpcFalsyResponseThenableCall_ForNonProtobufDescriptor (line 128) | async testRpcFalsyResponseThenableCall_ForNonProtobufDescriptor() {
  method testCancelledThenableCall (line 151) | async testCancelledThenableCall() {
  method testCancelledThenableCallWithReason (line 177) | async testCancelledThenableCallWithReason() {
  method testDeadline (line 202) | async testDeadline() {
  method testRpcError (line 226) | async testRpcError() {
  method testRpcDeserializationError (line 250) | async testRpcDeserializationError() {
  method testRpcResponseHeader (line 273) | async testRpcResponseHeader() {
  method testStreamInterceptor (line 300) | async testStreamInterceptor() {
  class MockRequest (line 328) | class MockRequest {
    method constructor (line 332) | constructor(data = '') {
  class MockReply (line 339) | class MockReply {
    method constructor (line 343) | constructor(data = '') {
  function createMethodDescriptor (line 363) | function createMethodDescriptor(responseDeSerializeFn) {
  class StreamResponseInterceptor (line 374) | class StreamResponseInterceptor {
    method constructor (line 375) | constructor() {}
    method intercept (line 385) | intercept(request, invoker) {
  class InterceptedStream (line 395) | class InterceptedStream {
    method constructor (line 399) | constructor(stream) {
    method on (line 410) | on(eventType, callback) {
    method cancel (line 427) | cancel() {
    method removeListener (line 438) | removeListener(eventType, callback) {

FILE: javascript/net/grpc/web/grpcwebclientreadablestream.js
  constant GRPC_STATUS (line 49) | const GRPC_STATUS = 'grpc-status';
  constant GRPC_STATUS_MESSAGE (line 50) | const GRPC_STATUS_MESSAGE = 'grpc-message';
  constant EXCLUDED_RESPONSE_HEADERS (line 53) | const EXCLUDED_RESPONSE_HEADERS =
  class GrpcWebClientReadableStream (line 64) | class GrpcWebClientReadableStream {
    method constructor (line 69) | constructor(genericTransportInterface) {
    method on (line 294) | on(eventType, callback) {
    method removeListenerFromCallbacks_ (line 315) | removeListenerFromCallbacks_(callbacks, callback) {
    method removeListener (line 326) | removeListener(eventType, callback) {
    method setResponseDeserializeFn (line 347) | setResponseDeserializeFn(responseDeserializeFn) {
    method cancel (line 355) | cancel() {
    method parseHttp1Headers_ (line 367) | parseHttp1Headers_(str) {
    method handleError_ (line 384) | handleError_(error) {
    method sendDataCallbacks_ (line 400) | sendDataCallbacks_(data) {
    method sendStatusCallbacks_ (line 410) | sendStatusCallbacks_(status) {
    method sendMetadataCallbacks_ (line 420) | sendMetadataCallbacks_(metadata) {
    method sendErrorCallbacks_ (line 430) | sendErrorCallbacks_(error) {
    method sendEndCallbacks_ (line 439) | sendEndCallbacks_() {

FILE: javascript/net/grpc/web/grpcwebstreamparser.js
  class GrpcWebStreamParser (line 55) | class GrpcWebStreamParser {
    method constructor (line 56) | constructor() {
    method isInputValid (line 116) | isInputValid() {
    method getErrorMessage (line 123) | getErrorMessage() {
    method acceptsBinaryInput (line 131) | acceptsBinaryInput() {
    method parse (line 147) | parse(input) {

FILE: javascript/net/grpc/web/methoddescriptor.js
  method constructor (line 35) | constructor(
  method createRequest (line 59) | createRequest(
  method createUnaryResponse (line 71) | createUnaryResponse(responseMessage, metadata = {}, status = null) {
  method getName (line 79) | getName() {
  method getMethodType (line 86) | getMethodType() {
  method getResponseMessageCtor (line 94) | getResponseMessageCtor() {
  method getRequestMessageCtor (line 102) | getRequestMessageCtor() {
  method getResponseDeserializeFn (line 107) | getResponseDeserializeFn() {
  method getRequestSerializeFn (line 112) | getRequestSerializeFn() {

FILE: javascript/net/grpc/web/request.js
  class Request (line 16) | class Request {
    method getRequestMessage (line 21) | getRequestMessage() {}
    method getMethodDescriptor (line 27) | getMethodDescriptor() {}
    method getMetadata (line 33) | getMetadata() {}
    method getCallOptions (line 42) | getCallOptions() {}
    method withMetadata (line 49) | withMetadata(key, value) {}
    method withGrpcCallOption (line 57) | withGrpcCallOption(name, value) {}

FILE: javascript/net/grpc/web/requestinternal.js
  class RequestInternal (line 18) | class RequestInternal {
    method constructor (line 25) | constructor(requestMessage, methodDescriptor, metadata, callOptions) {
    method getRequestMessage (line 49) | getRequestMessage() {
    method getMethodDescriptor (line 57) | getMethodDescriptor() {
    method getMetadata (line 65) | getMetadata() {
    method getCallOptions (line 73) | getCallOptions() {
    method withMetadata (line 80) | withMetadata(key, value) {
    method withGrpcCallOption (line 88) | withGrpcCallOption(name, value) {

FILE: javascript/net/grpc/web/rpcerror.js
  class RpcError (line 35) | class RpcError extends Error {
    method constructor (line 41) | constructor(code, message, metadata = {}) {
    method toString (line 50) | toString() {

FILE: javascript/net/grpc/web/status.js
  function Status (line 30) | function Status() {}

FILE: javascript/net/grpc/web/statuscode_test.js
  method testFromHttpStatus (line 25) | testFromHttpStatus() {
  method testGetHttpStatus (line 31) | testGetHttpStatus() {
  method testUnknown (line 37) | testUnknown() {

FILE: javascript/net/grpc/web/unaryresponse.js
  class UnaryResponse (line 16) | class UnaryResponse {
    method getResponseMessage (line 21) | getResponseMessage() {}
    method getMetadata (line 27) | getMetadata() {}
    method getMethodDescriptor (line 33) | getMethodDescriptor() {}
    method getStatus (line 41) | getStatus() {}

FILE: javascript/net/grpc/web/unaryresponseinternal.js
  class UnaryResponseInternal (line 19) | class UnaryResponseInternal {
    method constructor (line 26) | constructor(responseMessage, methodDescriptor, metadata = {}, status =...
    method getResponseMessage (line 53) | getResponseMessage() {
    method getMetadata (line 58) | getMetadata() {
    method getMethodDescriptor (line 63) | getMethodDescriptor() {
    method getStatus (line 68) | getStatus() {

FILE: net/grpc/gateway/examples/echo/echo_server.cc
  function RunServer (line 29) | void RunServer() {
  function main (line 39) | int main(int argc, char** argv) {

FILE: net/grpc/gateway/examples/echo/echo_service_impl.cc
  function Status (line 55) | Status EchoServiceImpl::Echo(ServerContext* context, const EchoRequest* ...
  function Status (line 62) | Status EchoServiceImpl::EchoAbort(ServerContext* context,
  function Status (line 71) | Status EchoServiceImpl::NoOp(ServerContext* context, const Empty* request,
  function Status (line 77) | Status EchoServiceImpl::ServerStreamingEcho(
  function Status (line 93) | Status EchoServiceImpl::ServerStreamingEchoAbort(

FILE: net/grpc/gateway/examples/echo/node-server/server.js
  function copyMetadata (line 41) | function copyMetadata(call) {
  function doEcho (line 54) | function doEcho(call, callback) {
  function doEchoAbort (line 64) | function doEchoAbort(call, callback) {
  function doServerStreamingEcho (line 74) | function doServerStreamingEcho(call) {
  function getServer (line 97) | function getServer() {

FILE: net/grpc/gateway/examples/echo/ts-example/client.ts
  class EchoApp (line 31) | class EchoApp {
    method constructor (line 37) | constructor(public echoService: EchoServiceClient) {}
    method addMessage (line 39) | static addMessage(message: string, cssClass: string) {
    method addLeftMessage (line 44) | static addLeftMessage(message: string) {
    method addRightMessage (line 48) | static addRightMessage(message: string) {
    method echo (line 52) | echo(msg: string) {
    method echoError (line 78) | echoError(msg: string) {
    method cancel (line 92) | cancel() {
    method repeatEcho (line 99) | repeatEcho(msg: string, count: number) {
    method send (line 130) | send(e: {}) {
    method load (line 152) | load() {

FILE: net/grpc/gateway/examples/helloworld/debugging/node-client.js
  function runSayHello (line 25) | function runSayHello(callback) {
  function runSayRepeatHello (line 35) | function runSayRepeatHello(callback) {
  function main (line 49) | function main() {

FILE: net/grpc/gateway/examples/helloworld/server.js
  function doSayHello (line 41) | function doSayHello(call, callback) {
  function doSayRepeatHello (line 48) | function doSayRepeatHello(call) {
  function getServer (line 69) | function getServer() {

FILE: packages/grpc-web/index.d.ts
  type Metadata (line 3) | interface Metadata { [s: string]: string; }
  class AbstractClientBase (line 5) | class AbstractClientBase {
  class ClientReadableStream (line 37) | class ClientReadableStream<RESP> {
  type StreamInterceptor (line 63) | interface StreamInterceptor<REQ, RESP> {
  type UnaryInterceptor (line 69) | interface UnaryInterceptor<REQ, RESP> {
  type PromiseCallOptions (line 76) | interface PromiseCallOptions {
  class MethodDescriptor (line 81) | class MethodDescriptor<REQ, RESP> {
  class Request (line 91) | class Request<REQ, RESP> {
  class UnaryResponse (line 97) | class UnaryResponse<REQ, RESP> {
  type GrpcWebClientBaseOptions (line 104) | interface GrpcWebClientBaseOptions {
  class GrpcWebClientBase (line 112) | class GrpcWebClientBase extends AbstractClientBase {
  class RpcError (line 116) | class RpcError extends Error {
  type Status (line 122) | interface Status {
  type StatusCode (line 128) | enum StatusCode {

FILE: packages/grpc-web/scripts/build.js
  function createSymlink (line 56) | function createSymlink(target, path) {

FILE: packages/grpc-web/scripts/common.py
  function read_file (line 23) | def read_file(path: str) -> str:
  function write_file (line 29) | def write_file(path: str, content: str):
  function get_files_with_suffix (line 40) | def get_files_with_suffix(root_dir: str, suffix: str) -> Iterator[str]:

FILE: packages/grpc-web/scripts/gen_all_tests_js.py
  function main (line 37) | def main():

FILE: packages/grpc-web/scripts/gen_test_htmls.py
  function main (line 33) | def main():
  function _gen_test_html (line 42) | def _gen_test_html(js_file_path: str, template: Template):
  function _extract_closure_package (line 63) | def _extract_closure_package(js_file_path) -> str:

FILE: packages/grpc-web/test/common.js
  constant GENERATED_CODE_PATH (line 20) | const GENERATED_CODE_PATH = './generated';

FILE: packages/grpc-web/test/eval_test.js
  constant GENERATED_CODE_PATH (line 25) | const GENERATED_CODE_PATH = require('./common.js').GENERATED_CODE_PATH;

FILE: packages/grpc-web/test/generated_code_test.js
  constant GENERATED_CODE_PATH (line 25) | const GENERATED_CODE_PATH = require('./common.js').GENERATED_CODE_PATH;
  function multiDone (line 30) | function multiDone(done, count) {

FILE: packages/grpc-web/test/plugin_test.js
  constant GENERATED_CODE_PATH (line 25) | const GENERATED_CODE_PATH = require('./common.js').GENERATED_CODE_PATH;

FILE: packages/grpc-web/test/tsc-tests/client03.ts
  class MyStreamInterceptor (line 24) | class MyStreamInterceptor implements grpcWeb.StreamInterceptor<
    method intercept (line 26) | intercept(

FILE: packages/grpc-web/test/tsc-tests/client04.ts
  class MyUnaryInterceptor (line 24) | class MyUnaryInterceptor implements grpcWeb.UnaryInterceptor<
    method intercept (line 26) | intercept(request: grpcWeb.Request<EchoRequest, EchoResponse>,

FILE: packages/grpc-web/test/tsc_test.js
  function relativePath (line 28) | function relativePath(relPath) {
  function cleanup (line 31) | function cleanup() {
  function createGeneratedCodeDir (line 35) | function createGeneratedCodeDir() {
  function assertFileExists (line 38) | function assertFileExists(relPath) {
  function multiDone (line 41) | function multiDone(done, count) {
  function runTscCmd (line 49) | function runTscCmd(tscCmd) {

FILE: scripts/release_notes.py
  class LabelLevel (line 67) | class LabelLevel(Enum):
    method __gt__ (line 72) | def __gt__(self, other):
    method __lt__ (line 76) | def __lt__(self, other):
  class ReleaseNotes (line 82) | class ReleaseNotes:
    method __init__ (line 83) | def __init__(self):
  class ProcessChangelog (line 90) | class ProcessChangelog:
    method __init__ (line 91) | def __init__(self):
    method github_api (line 102) | def github_api(self, url):
    method check_release (line 112) | def check_release(self, sha):
    method _get_pr_label_level (line 123) | def _get_pr_label_level(self, labels):
    method get_releases (line 138) | def get_releases(self):
    method get_merged_prs (line 163) | def get_merged_prs(self, num_pages):
    method format_release_notes (line 209) | def format_release_notes(self):
    method print_changelog (line 237) | def print_changelog(self, output_without_labels, output_unreleased_only):
  function build_args_parser (line 277) | def build_args_parser():
  function main (line 298) | def main():

FILE: test/interop/interop_client.js
  constant SERVER_HOST (line 32) | const SERVER_HOST = 'http://localhost:8080';
  constant TIMEOUT_MS (line 33) | const TIMEOUT_MS = 1000;
  function multiDone (line 35) | function multiDone(done, count) {
  function doEmptyUnary (line 44) | function doEmptyUnary(done) {
  function doEmptyUnaryWithDeadline (line 53) | function doEmptyUnaryWithDeadline(done) {
  function doLargeUnary (line 66) | function doLargeUnary(done) {
  function doServerStreaming (line 84) | function doServerStreaming(done) {
  function doCustomMetadata (line 109) | function doCustomMetadata(done) {
  function doStatusCodeAndMessage (line 149) | function doStatusCodeAndMessage(done) {
  function doUnimplementedMethod (line 170) | function doUnimplementedMethod(done) {
Condensed preview — 174 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (509K chars).
[
  {
    "path": ".bazelci/presubmit.yml",
    "chars": 268,
    "preview": "---\n# TODO(yannic): Enable buildifier and test on Windows and RBE (both unsupported by rules_closure).\nplatforms:\n  maco"
  },
  {
    "path": ".bazelignore",
    "chars": 54,
    "preview": "# //third_party conatins git submodules.\nthird_party/\n"
  },
  {
    "path": ".bazelrc",
    "chars": 401,
    "preview": "# Common build settings for unix-like systems\nbuild --copt=-Wno-error=deprecated-declarations\nbuild --host_copt=-Wno-err"
  },
  {
    "path": ".bazelrc.windows",
    "chars": 580,
    "preview": "# This is a Windows-specific bazelrc file for use in GitHub Actions.\n# It contains settings from the main .bazelrc file "
  },
  {
    "path": ".dockerignore",
    "chars": 52,
    "preview": "**/dist\n**/node_modules\npackages/grpc-web/generated\n"
  },
  {
    "path": ".github/workflows/make-plugin-linux.yml",
    "chars": 3251,
    "preview": "name: Make Linux Plugin\n\non:\n  push:\n    paths:\n      - .github/workflows/make-plugin-linux.yml\n  pull_request:\n    path"
  },
  {
    "path": ".github/workflows/make-plugin-mac-os.yml",
    "chars": 2443,
    "preview": "name: Make macOS Plugin\n\non:\n  push:\n    paths:\n      - .github/workflows/make-plugin-mac-os.yml\n  pull_request:\n    pat"
  },
  {
    "path": ".github/workflows/make-plugin-windows.yml",
    "chars": 4055,
    "preview": "name: Make Windows Plugin\n\non:\n  push:\n    paths:\n      - .github/workflows/make-plugin-windows.yml\n  pull_request:\n    "
  },
  {
    "path": ".github/workflows/release-source-archive.yml",
    "chars": 1754,
    "preview": "name: Publish Stable Source Archive\n\non:\n  release:\n    types: [published]\n\njobs:\n  # Whenever a release is published, t"
  },
  {
    "path": ".gitignore",
    "chars": 166,
    "preview": ".vscode\nbazel-bin\nbazel-genfiles\nbazel-grpc-web\nbazel-out\nbazel-testlogs\n*.o\nprotoc-gen-*\n.DS_Store\ntarget\n.project\n.cla"
  },
  {
    "path": ".gitmodules",
    "chars": 119,
    "preview": "[submodule \"third_party/protobuf\"]\n\tpath = third_party/protobuf\n\turl = https://github.com/protocolbuffers/protobuf.git\n"
  },
  {
    "path": "AUTHORS",
    "chars": 12,
    "preview": "Google Inc.\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 21574,
    "preview": "[//]: # (GENERATED FILE -- DO NOT EDIT!)\n[//]: # (See scripts/release_notes.py for more details.)\n\n## 2.0.2\n\n- [#1507](h"
  },
  {
    "path": "CODE-OF-CONDUCT.md",
    "chars": 142,
    "preview": "## Community Code of Conduct\n\ngRPC-Web follows the\n[CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 701,
    "preview": "# How to contribute\n\nWe definitely welcome patches and contribution to gRPC-Web! Here is some guideline\nand information "
  },
  {
    "path": "GOVERNANCE.md",
    "chars": 141,
    "preview": "This repository is governed by the gRPC organization's [governance rules](https://github.com/grpc/grpc-community/blob/ma"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "MAINTAINERS.md",
    "chars": 728,
    "preview": "This page lists all active maintainers of this repository. If you were a\nmaintainer and would like to add your name to t"
  },
  {
    "path": "MODULE.bazel",
    "chars": 712,
    "preview": "\"\"\"\nA bazel module for the grpc-web project.\n\nVisit https://grpc.io/ and https://github.com/grpc/grpc-web for\nmore infor"
  },
  {
    "path": "Makefile",
    "chars": 283,
    "preview": "ROOT_DIR := $(shell pwd)\n\nall: clean\n\nplugin:\n\tcd \"$(ROOT_DIR)\"/javascript/net/grpc/web/generator && make\n\ninstall-plugi"
  },
  {
    "path": "PATENTS",
    "chars": 1315,
    "preview": "Additional IP Rights Grant (Patents)\n\n\"This implementation\" means the copyrightable works distributed by\nGoogle as part "
  },
  {
    "path": "README.md",
    "chars": 13860,
    "preview": "# gRPC Web &middot; [![npm version](https://img.shields.io/npm/v/grpc-web.svg?style=flat)](https://www.npmjs.com/package"
  },
  {
    "path": "SECURITY.md",
    "chars": 202,
    "preview": "# Security Policy\n\nFor information on gRPC Security Policy and reporting potentional security issues, please see [gRPC C"
  },
  {
    "path": "doc/browser-features.md",
    "chars": 3000,
    "preview": "# gRPC-Web features for browser (HTML) clients\n\nDue to browser limitation, gRPC-Web uses a different transport\nthan the "
  },
  {
    "path": "doc/in-process-proxy.md",
    "chars": 2077,
    "preview": "# Overview\n\nIn-process proxies allow a browser client to talk to a gRPC server directly without relying on any intermedi"
  },
  {
    "path": "doc/interop-test-descriptions.md",
    "chars": 3165,
    "preview": "gRPC-Web Interop Tests\n======================\n\nThis document describes the set of tests any gRPC-Web clients or proxies "
  },
  {
    "path": "doc/roadmap.md",
    "chars": 2744,
    "preview": "# gRPC-Web Roadmap\n\nThe purpose of this document is to collect all the features that we believe are\nuseful for gRPC user"
  },
  {
    "path": "doc/streaming-roadmap.md",
    "chars": 2100,
    "preview": "# Streaming Roadmap\n\nThis document describes the road-map for gRPC-Web to support different streaming features.\n* Server"
  },
  {
    "path": "docker-compose.yml",
    "chars": 2351,
    "preview": "version: '3'\nservices:\n  prereqs:\n    build:\n      context: ./\n      dockerfile: ./net/grpc/gateway/docker/prereqs/Docke"
  },
  {
    "path": "etc/localhost.crt",
    "chars": 1086,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIC9jCCAd4CCQCfXxHXagE8mjANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQGEwJV\nUzELMAkGA1UECAwCQ0ExITAfBgN"
  },
  {
    "path": "etc/localhost.key",
    "chars": 1675,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEA2/MlKj+OtIgJm/7DywORPOypfvGHXqHTpg/ZbZgflx2vMwgo\nAhdun2e//AlssouStadnkev"
  },
  {
    "path": "javascript/net/grpc/web/abstractclientbase.js",
    "chars": 3476,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/calloptions.js",
    "chars": 1300,
    "preview": "/**\n * @fileoverview grpc.web.CallOptions\n */\n\ngoog.module('grpc.web.CallOptions');\ngoog.module.declareLegacyNamespace()"
  },
  {
    "path": "javascript/net/grpc/web/clientoptions.js",
    "chars": 1836,
    "preview": "goog.module('grpc.web.ClientOptions');\ngoog.module.declareLegacyNamespace();\n\nconst {StreamInterceptor, UnaryInterceptor"
  },
  {
    "path": "javascript/net/grpc/web/clientreadablestream.js",
    "chars": 2704,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/clientunarycallimpl.js",
    "chars": 1014,
    "preview": "/**\n * @fileoverview This class handles ClientReadableStream returned by unary\n * calls.\n */\n\ngoog.module('grpc.web.Clie"
  },
  {
    "path": "javascript/net/grpc/web/generator/BUILD.bazel",
    "chars": 248,
    "preview": "load(\"@rules_cc//cc:defs.bzl\", \"cc_binary\")\n\ncc_binary(\n    name = \"protoc-gen-grpc-web\",\n    srcs = [\n        \"grpc_gen"
  },
  {
    "path": "javascript/net/grpc/web/generator/Makefile",
    "chars": 1261,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "javascript/net/grpc/web/generator/grpc_generator.cc",
    "chars": 64448,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/generictransportinterface.js",
    "chars": 1223,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/grpcwebclientbase.js",
    "chars": 14078,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/grpcwebclientbase_test.js",
    "chars": 15403,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/grpcwebclientreadablestream.js",
    "chars": 13639,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/grpcwebstreamparser.js",
    "chars": 7382,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/grpcwebstreamparser_test.js",
    "chars": 6935,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/interceptor.js",
    "chars": 2485,
    "preview": "/**\n * @fileoverview grpc-web client interceptors.\n *\n * The type of interceptors is determined by the response type of "
  },
  {
    "path": "javascript/net/grpc/web/metadata.js",
    "chars": 284,
    "preview": "/**\n * @fileoverview grpc-web request/response metadata.\n *\n * Request and response headers will be included in the Meta"
  },
  {
    "path": "javascript/net/grpc/web/methoddescriptor.js",
    "chars": 3036,
    "preview": "/**\n * @fileoverview Description of this file.\n *\n * A templated class that is used to address gRPC Web requests.\n */\n\ng"
  },
  {
    "path": "javascript/net/grpc/web/methoddescriptorinterface.js",
    "chars": 1898,
    "preview": "/**\n * @fileoverview Description of this file.\n *\n * A templated class that is used to address gRPC Web requests.\n */\n\ng"
  },
  {
    "path": "javascript/net/grpc/web/methodtype.js",
    "chars": 577,
    "preview": "/**\n * @fileoverview gRPC-Web method types.\n */\n\ngoog.module('grpc.web.MethodType');\n\ngoog.module.declareLegacyNamespace"
  },
  {
    "path": "javascript/net/grpc/web/request.js",
    "chars": 1285,
    "preview": "/**\n * @fileoverview  A templated class that is used to address an individual\n * gRPC-Web request instance.\n */\ngoog.mod"
  },
  {
    "path": "javascript/net/grpc/web/requestinternal.js",
    "chars": 1886,
    "preview": "/**\n * @fileoverview Internal implementation of grpc.web.Request.\n */\ngoog.module('grpc.web.RequestInternal');\ngoog.modu"
  },
  {
    "path": "javascript/net/grpc/web/rpcerror.js",
    "chars": 1746,
    "preview": "/**\n *\n * Copyright 2021 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/status.js",
    "chars": 1073,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/statuscode.js",
    "chars": 9247,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "javascript/net/grpc/web/statuscode_test.js",
    "chars": 1189,
    "preview": "goog.module('grpc.web.StatusCodeTest');\ngoog.setTestOnly('grpc.web.StatusCodeTest');\n\nconst StatusCode = goog.require('g"
  },
  {
    "path": "javascript/net/grpc/web/unaryresponse.js",
    "chars": 884,
    "preview": "/**\n * @fileoverview gRPC web client UnaryResponse returned by grpc unary calls.\n */\n\ngoog.module('grpc.web.UnaryRespons"
  },
  {
    "path": "javascript/net/grpc/web/unaryresponseinternal.js",
    "chars": 1565,
    "preview": "/**\n * @fileoverview gRPC-Web UnaryResponse internal implementation.\n */\n\ngoog.module('grpc.web.UnaryResponseInternal');"
  },
  {
    "path": "kokoro/interop.cfg",
    "chars": 52,
    "preview": "build_file: \"grpc-web/scripts/run_interop_tests.sh\"\n"
  },
  {
    "path": "kokoro/master.cfg",
    "chars": 41,
    "preview": "build_file: \"grpc-web/scripts/kokoro.sh\"\n"
  },
  {
    "path": "kokoro/presubmit.cfg",
    "chars": 50,
    "preview": "build_file: \"grpc-web/scripts/run_basic_tests.sh\"\n"
  },
  {
    "path": "net/grpc/gateway/docker/binary_client/Dockerfile",
    "chars": 1105,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/docker/closure_client/Dockerfile",
    "chars": 789,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/docker/commonjs_client/Dockerfile",
    "chars": 1109,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/docker/echo_server/Dockerfile",
    "chars": 775,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/docker/envoy/Dockerfile",
    "chars": 796,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/docker/grpcwebproxy/Dockerfile",
    "chars": 1346,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/docker/interop_client/Dockerfile",
    "chars": 1105,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/docker/node_interop_server/Dockerfile",
    "chars": 1153,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/docker/node_server/Dockerfile",
    "chars": 722,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/docker/prereqs/Dockerfile",
    "chars": 3522,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/docker/ts_client/Dockerfile",
    "chars": 1391,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/examples/echo/.gitignore",
    "chars": 33,
    "preview": "/package-lock.json\n/node_modules\n"
  },
  {
    "path": "net/grpc/gateway/examples/echo/BUILD.bazel",
    "chars": 796,
    "preview": "load(\"@com_github_grpc_grpc//bazel:cc_grpc_library.bzl\", \"cc_grpc_library\")\nload(\"@rules_cc//cc:defs.bzl\", \"cc_binary\", "
  },
  {
    "path": "net/grpc/gateway/examples/echo/Makefile",
    "chars": 1935,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "net/grpc/gateway/examples/echo/README.md",
    "chars": 1724,
    "preview": "## Build and Run an Echo example\n\nThis page will show you how to quickly build and run an end-to-end Echo\nexample. The e"
  },
  {
    "path": "net/grpc/gateway/examples/echo/commonjs-example/.gitignore",
    "chars": 31,
    "preview": "node_modules\npackage-lock.json\n"
  },
  {
    "path": "net/grpc/gateway/examples/echo/commonjs-example/client.js",
    "chars": 2262,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "net/grpc/gateway/examples/echo/commonjs-example/echotest.html",
    "chars": 1657,
    "preview": "<!-- Copyright 2018 Google LLC  -->\n\n<!-- Licensed under the Apache License, Version 2.0 (the \"License\");  -->\n<!-- you "
  },
  {
    "path": "net/grpc/gateway/examples/echo/commonjs-example/package.json",
    "chars": 310,
    "preview": "{\n  \"name\": \"grpc-web-commonjs-example\",\n  \"version\": \"0.1.0\",\n  \"description\": \"gRPC-Web CommonJS client example\",\n  \"l"
  },
  {
    "path": "net/grpc/gateway/examples/echo/commonjs-example/webpack.config.js",
    "chars": 68,
    "preview": "module.exports = {\n  mode: \"production\",\n  entry: \"./client.js\",\n};\n"
  },
  {
    "path": "net/grpc/gateway/examples/echo/echo.proto",
    "chars": 3756,
    "preview": "// Copyright 2018 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "net/grpc/gateway/examples/echo/echo_chat.js",
    "chars": 79,
    "preview": "// TODO: Implement simple chat client using ClosureJS (similar to echoapp.js).\n"
  },
  {
    "path": "net/grpc/gateway/examples/echo/echo_server.cc",
    "chars": 1214,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "net/grpc/gateway/examples/echo/echo_service_impl.cc",
    "chars": 3667,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "net/grpc/gateway/examples/echo/echo_service_impl.h",
    "chars": 2164,
    "preview": "#ifndef NET_GRPC_GATEWAY_EXAMPLES_ECHO_ECHO_SERVICE_IMPL_H_\n#define NET_GRPC_GATEWAY_EXAMPLES_ECHO_ECHO_SERVICE_IMPL_H_\n"
  },
  {
    "path": "net/grpc/gateway/examples/echo/echoapp.js",
    "chars": 4762,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "net/grpc/gateway/examples/echo/echotest.html",
    "chars": 1977,
    "preview": "<!-- Copyright 2018 Google LLC  -->\n\n<!-- Licensed under the Apache License, Version 2.0 (the \"License\");  -->\n<!-- you "
  },
  {
    "path": "net/grpc/gateway/examples/echo/envoy.yaml",
    "chars": 2803,
    "preview": "admin:\n  access_log_path: /tmp/admin_access.log\n  address:\n    socket_address: { address: 0.0.0.0, port_value: 9901 }\n\ns"
  },
  {
    "path": "net/grpc/gateway/examples/echo/node-server/.gitignore",
    "chars": 32,
    "preview": "node_modules/\npackage-lock.json\n"
  },
  {
    "path": "net/grpc/gateway/examples/echo/node-server/package.json",
    "chars": 263,
    "preview": "{\n  \"name\": \"grpc-web-node-server-example\",\n  \"version\": \"0.1.0\",\n  \"main\": \"server.js\",\n  \"dependencies\": {\n    \"@grpc/"
  },
  {
    "path": "net/grpc/gateway/examples/echo/node-server/server.js",
    "chars": 2890,
    "preview": "/*\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not "
  },
  {
    "path": "net/grpc/gateway/examples/echo/package.json",
    "chars": 307,
    "preview": "{\n    \"name\": \"echo-closure-example\",\n    \"version\": \"0.1.0\",\n    \"description\": \"gRPC-Web Closure JS client example\",\n "
  },
  {
    "path": "net/grpc/gateway/examples/echo/ts-example/.gitignore",
    "chars": 38,
    "preview": "dist/\nnode_modules/\npackage-lock.json\n"
  },
  {
    "path": "net/grpc/gateway/examples/echo/ts-example/README.md",
    "chars": 1338,
    "preview": "# Instructions to run the Typescript example\n\n## Docker run\n\n```bash\n# From root dir\ndocker-compose up --build node-serv"
  },
  {
    "path": "net/grpc/gateway/examples/echo/ts-example/client.ts",
    "chars": 5158,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "net/grpc/gateway/examples/echo/ts-example/echotest.html",
    "chars": 1657,
    "preview": "<!-- Copyright 2018 Google LLC  -->\n\n<!-- Licensed under the Apache License, Version 2.0 (the \"License\");  -->\n<!-- you "
  },
  {
    "path": "net/grpc/gateway/examples/echo/ts-example/package.json",
    "chars": 336,
    "preview": "{\n  \"devDependencies\": {\n    \"@types/google-protobuf\": \"~3.15.12\",\n    \"@types/jquery\": \"~3.3.6\",\n    \"@types/node\": \"la"
  },
  {
    "path": "net/grpc/gateway/examples/echo/ts-example/tsconfig.json",
    "chars": 365,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"module\": \"commonjs\",\n    \"strict\": true,\n    \"allowJs\": true,\n    \"ou"
  },
  {
    "path": "net/grpc/gateway/examples/echo/ts-example/webpack.config.js",
    "chars": 73,
    "preview": "module.exports = {\n  mode: 'production',\n  entry: './dist/client.js',\n};\n"
  },
  {
    "path": "net/grpc/gateway/examples/echo/tutorial.md",
    "chars": 5681,
    "preview": "This tutorial provides a detailed guide on how to run a gRPC service and access\nit in the browser.\n\n\n## Define the Servi"
  },
  {
    "path": "net/grpc/gateway/examples/helloworld/.gitignore",
    "chars": 46,
    "preview": "dist/\n*_pb.js\nnode_modules/\npackage-lock.json\n"
  },
  {
    "path": "net/grpc/gateway/examples/helloworld/README.md",
    "chars": 10467,
    "preview": "# gRPC-Web Hello World Guide\n\nThis guide is intended to help you get started with gRPC-Web with a simple\nHello World exa"
  },
  {
    "path": "net/grpc/gateway/examples/helloworld/client.js",
    "chars": 1615,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "net/grpc/gateway/examples/helloworld/debugging/node-client.js",
    "chars": 1351,
    "preview": "// NOTE: This client is used for debugging the node gRPC server WITHOUT the\n// Envoy proxy. It does not use the gRPC-Web"
  },
  {
    "path": "net/grpc/gateway/examples/helloworld/envoy.yaml",
    "chars": 2910,
    "preview": "admin:\n  access_log_path: /tmp/admin_access.log\n  address:\n    socket_address: { address: 0.0.0.0, port_value: 9901 }\n\ns"
  },
  {
    "path": "net/grpc/gateway/examples/helloworld/helloworld.proto",
    "chars": 975,
    "preview": "// Copyright 2018 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "net/grpc/gateway/examples/helloworld/index.html",
    "chars": 237,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<title>gRPC-Web Example</title>\n<script src=\"./dist/main."
  },
  {
    "path": "net/grpc/gateway/examples/helloworld/package.json",
    "chars": 386,
    "preview": "{\n  \"name\": \"grpc-web-simple-example\",\n  \"version\": \"0.1.0\",\n  \"description\": \"gRPC-Web simple example\",\n  \"main\": \"serv"
  },
  {
    "path": "net/grpc/gateway/examples/helloworld/server.js",
    "chars": 2174,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/.gitignore",
    "chars": 68,
    "preview": "/index.js\n/generated/\n\npackage-lock.json\nnode_modules/\n__pycache__/\n"
  },
  {
    "path": "packages/grpc-web/.npmignore",
    "chars": 40,
    "preview": "/scripts\n/package-lock.json\n/.gitignore\n"
  },
  {
    "path": "packages/grpc-web/README.md",
    "chars": 3937,
    "preview": "# gRPC-Web Client Runtime Library\n\ngRPC-Web provides a Javascript library that lets browser clients access a gRPC\nservic"
  },
  {
    "path": "packages/grpc-web/docker/jsunit-test/Dockerfile",
    "chars": 763,
    "preview": "FROM selenium/standalone-chrome:112.0.5615.165\n\nARG NODE_VERSION=20.0.0\nUSER root\n\n# Install Node.js dependencies\nRUN ap"
  },
  {
    "path": "packages/grpc-web/exports.js",
    "chars": 1240,
    "preview": "/**\n * @fileoverview Export symbols needed by generated code in CommonJS style.\n *\n * Note that public methods called by"
  },
  {
    "path": "packages/grpc-web/externs.js",
    "chars": 1548,
    "preview": "var module;\n\n/**\n * List of functions we want to preserve when running the closure compiler\n * with --compilation_level="
  },
  {
    "path": "packages/grpc-web/gulpfile.js",
    "chars": 238,
    "preview": "const connect = require('gulp-connect');\nconst gulp = require('gulp');\n\ngulp.task('serve', () => {\n  connect.server({\n  "
  },
  {
    "path": "packages/grpc-web/index.d.ts",
    "chars": 4558,
    "preview": "declare module \"grpc-web\" {\r\n\r\n  export interface Metadata { [s: string]: string; }\r\n\r\n  export class AbstractClientBase"
  },
  {
    "path": "packages/grpc-web/package.json",
    "chars": 1263,
    "preview": "{\n  \"name\": \"grpc-web\",\n  \"version\": \"2.0.2\",\n  \"author\": \"Google Inc.\",\n  \"description\": \"gRPC-Web Client Runtime Libra"
  },
  {
    "path": "packages/grpc-web/protractor.conf.js",
    "chars": 1438,
    "preview": "/**\n * Copyright 2021 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "packages/grpc-web/protractor_spec.js",
    "chars": 3488,
    "preview": "/**\n * Copyright 2021 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": "packages/grpc-web/scripts/build.js",
    "chars": 2036,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/scripts/common.py",
    "chars": 1587,
    "preview": "# Copyright 2021 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# "
  },
  {
    "path": "packages/grpc-web/scripts/gen_all_tests_js.py",
    "chars": 2078,
    "preview": "# Copyright 2021 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# "
  },
  {
    "path": "packages/grpc-web/scripts/gen_test_htmls.py",
    "chars": 2603,
    "preview": "# Copyright 2021 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# "
  },
  {
    "path": "packages/grpc-web/scripts/generate_test_files.sh",
    "chars": 1455,
    "preview": "#!/bin/bash\n# Copyright 2021 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"L"
  },
  {
    "path": "packages/grpc-web/scripts/run_jsunit_tests.sh",
    "chars": 1680,
    "preview": "#!/bin/bash\n# Copyright 2021 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"L"
  },
  {
    "path": "packages/grpc-web/scripts/template_all_tests_js.txt",
    "chars": 123,
    "preview": "var allTests = [\n$test_html_paths\n];\nif (typeof module !== 'undefined' && module.exports) {\n  module.exports = allTests;"
  },
  {
    "path": "packages/grpc-web/scripts/template_test_html.txt",
    "chars": 386,
    "preview": "<!-- Copyright (c) 2021 Google Inc. All rights reserved. -->\n<!doctype html>\n<html>\n<head>\n  <meta http-equiv=\"X-UA-Comp"
  },
  {
    "path": "packages/grpc-web/test/closure_client.js",
    "chars": 1143,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/common.js",
    "chars": 1108,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/eval_test.js",
    "chars": 2994,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/export_test.js",
    "chars": 1403,
    "preview": "const assert = require('assert');\nconst grpc = {};\ngrpc.web = require('grpc-web');\n\ndescribe('grpc-web export test', fun"
  },
  {
    "path": "packages/grpc-web/test/generated_code_test.js",
    "chars": 30907,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/gulpfile.js",
    "chars": 180,
    "preview": "const gulp = require('gulp');\nconst gulpEval = require('gulp-eval');\n\ngulp.task('gen-code-eval-test', () =>\n  gulp.src('"
  },
  {
    "path": "packages/grpc-web/test/plugin_test.js",
    "chars": 7688,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/protos/echo.proto",
    "chars": 1482,
    "preview": "// Copyright 2018 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "packages/grpc-web/test/protos/foo.proto",
    "chars": 193,
    "preview": "syntax = \"proto3\";\n\npackage Foo;\n\nimport \"models.proto\";\n\nmessage SimpleRequest {\n  string message = 1;\n}\n\nservice FooSe"
  },
  {
    "path": "packages/grpc-web/test/protos/models.proto",
    "chars": 63,
    "preview": "syntax = \"proto3\";\n\npackage models;\n\nmessage SimpleMessage {\n}\n"
  },
  {
    "path": "packages/grpc-web/test/protos/myapi/v1/myapi-two.proto",
    "chars": 790,
    "preview": "// Copyright 2018 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "packages/grpc-web/test/protos/myapi/v1/myapi.proto",
    "chars": 884,
    "preview": "// Copyright 2018 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "packages/grpc-web/test/protos/nopackage.proto",
    "chars": 772,
    "preview": "// Copyright 2018 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "packages/grpc-web/test/protos/otherapi/v1/otherapi.proto",
    "chars": 684,
    "preview": "// Copyright 2018 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "packages/grpc-web/test/protos/test01.proto",
    "chars": 265,
    "preview": "syntax = \"proto3\";\n\nmessage MessageOuter {\n  message MessageInner {\n    int32 value = 1;\n  }\n\n  enum EnumInner {\n    DEF"
  },
  {
    "path": "packages/grpc-web/test/protos/test02.proto",
    "chars": 106,
    "preview": "syntax = \"proto3\";\nimport \"test03.proto\";\n\nservice MyService {\n  rpc addOne(Integer) returns (Integer);\n}\n"
  },
  {
    "path": "packages/grpc-web/test/protos/test03.proto",
    "chars": 40,
    "preview": "syntax = \"proto3\";\n\nmessage Integer {\n}\n"
  },
  {
    "path": "packages/grpc-web/test/tsc-tests/client01.ts",
    "chars": 851,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/tsc-tests/client02.ts",
    "chars": 926,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/tsc-tests/client03.ts",
    "chars": 2510,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/tsc-tests/client04.ts",
    "chars": 2240,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/tsc-tests/client05.ts",
    "chars": 1638,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/tsc-tests/client06.ts",
    "chars": 1216,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "packages/grpc-web/test/tsc_test.js",
    "chars": 13104,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "scripts/README.md",
    "chars": 1107,
    "preview": "# Scripts\n\nA collection of scripts (mostly test related).\n\n\n## Troubleshooting - Bazel Crashes (OOM)\n\n[Bazel](https://gi"
  },
  {
    "path": "scripts/docker-run-build-tests.sh",
    "chars": 887,
    "preview": "#!/bin/bash\n# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "scripts/docker-run-interop-tests.sh",
    "chars": 1341,
    "preview": "#!/bin/bash\n# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "scripts/docker-run-jsunit-tests.sh",
    "chars": 762,
    "preview": "#!/bin/bash\n# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "scripts/docker-run-mocha-tests.sh",
    "chars": 785,
    "preview": "#!/bin/bash\n# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "scripts/init_submodules.sh",
    "chars": 723,
    "preview": "#!/bin/bash\n# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "scripts/kokoro.sh",
    "chars": 705,
    "preview": "#!/bin/bash\n# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "scripts/release_notes.py",
    "chars": 12611,
    "preview": "# Copyright 2020 gRPC authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use thi"
  },
  {
    "path": "scripts/run_basic_tests.sh",
    "chars": 2742,
    "preview": "#!/bin/bash\n# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "scripts/run_interop_tests.sh",
    "chars": 1754,
    "preview": "#!/bin/bash\n# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "scripts/test-proxy.sh",
    "chars": 1962,
    "preview": "#!/bin/bash\n# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "src/proto/grpc/testing/empty.proto",
    "chars": 957,
    "preview": "\n// Copyright 2015 gRPC authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not us"
  },
  {
    "path": "src/proto/grpc/testing/messages.proto",
    "chars": 6900,
    "preview": "\n// Copyright 2015-2016 gRPC authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may n"
  },
  {
    "path": "src/proto/grpc/testing/test.proto",
    "chars": 3550,
    "preview": "\n// Copyright 2015-2016 gRPC authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may n"
  },
  {
    "path": "test/interop/.gitignore",
    "chars": 37,
    "preview": "node_modules/\npackage-lock.json\nsrc/\n"
  },
  {
    "path": "test/interop/README.md",
    "chars": 1195,
    "preview": "gRPC-Web Interop Tests\n======================\n\nSee the\n[main doc](https://github.com/grpc/grpc-web/blob/master/doc/inter"
  },
  {
    "path": "test/interop/envoy.yaml",
    "chars": 2885,
    "preview": "admin:\n  access_log_path: /tmp/admin_access.log\n  address:\n    socket_address: { address: 0.0.0.0, port_value: 9901 }\n\ns"
  },
  {
    "path": "test/interop/index.html",
    "chars": 1136,
    "preview": "<!-- Copyright 2018 Google LLC  -->\n\n<!-- Licensed under the Apache License, Version 2.0 (the \"License\");  -->\n<!-- you "
  },
  {
    "path": "test/interop/interop_client.js",
    "chars": 6674,
    "preview": "/**\n *\n * Copyright 2018 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not"
  },
  {
    "path": "test/interop/package.json",
    "chars": 458,
    "preview": "{\n  \"name\": \"grpc-web-interop-test\",\n  \"version\": \"0.1.0\",\n  \"description\": \"gRPC-Web Interop Test Client\",\n  \"license\":"
  },
  {
    "path": "test/interop/webpack.config.js",
    "chars": 76,
    "preview": "module.exports = {\n  mode: \"production\",\n  entry: \"./interop_client.js\",\n};\n"
  }
]

About this extraction

This page contains the full source code of the grpc/grpc-web GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 174 files (467.7 KB), approximately 123.1k tokens, and a symbol index with 292 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!