feat: add examples of Rust and TypeScript (#53)
* feat: demonstrate rust client/server
* feat: demonstrate TypeScript node client
* feat: add typescript server
* refactor: remove non-functional cc toolchain
Rust depends on having one that works. We can find a more surgical way to prevent protoc cc_binary from getting built.
* chore: missing file
* refactor: move to tools
diff --git a/.gitignore b/.gitignore
index 630e0fa..bc80243 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,6 @@
.bazelrc.user
.idea/
.ijwb/
+node_modules/
# Ignore until it is more stable
MODULE.bazel.lock
diff --git a/.prettierignore b/.prettierignore
index 2e117bf..57efff1 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1 +1,2 @@
docs/*.md
+examples/**/*_pb.d.ts
diff --git a/examples/.bazelignore b/examples/.bazelignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/examples/.bazelignore
@@ -0,0 +1 @@
+node_modules
diff --git a/examples/.bazelrc b/examples/.bazelrc
index 06d1d1b..6c89af4 100644
--- a/examples/.bazelrc
+++ b/examples/.bazelrc
@@ -4,3 +4,5 @@
# Force rules_go to disable CGO even though we have a (fake) C++ toolchain registered.
common --host_platform=//tools:no_cgo_host_platform
+common --@aspect_rules_ts//ts:skipLibCheck=always
+common --@aspect_rules_ts//ts:default_to_tsc_transpiler
diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel
index 5eda38d..20bd431 100644
--- a/examples/BUILD.bazel
+++ b/examples/BUILD.bazel
@@ -1,30 +1,19 @@
-load("@rules_go//proto:def.bzl", "go_proto_library")
-load("@rules_proto//proto:defs.bzl", "proto_library")
-load("@protobuf//bazel:py_proto_library.bzl", "py_proto_library")
+load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "copy_to_bin")
+load("@aspect_rules_ts//ts:defs.bzl", "ts_config")
+load("@npm//:defs.bzl", "npm_link_all_packages")
package(default_visibility = ["//visibility:public"])
-proto_library(
- name = "foo_proto",
- srcs = ["foo.proto"],
- deps = ["@com_google_protobuf//:any_proto"],
+copy_to_bin(
+ name = "package_json",
+ srcs = ["package.json"],
)
-py_proto_library(
- name = "foo_py_proto",
- deps = [":foo_proto"],
-)
+# Link all direct dependencies in /package.json to
+# bazel-bin/node_modules
+npm_link_all_packages(name = "node_modules")
-# Broken by https://github.com/protocolbuffers/protobuf/pull/19679
-# which causes building C++ code from source.
-# TODO: re-enable once protobuf honors the toolchain
-# java_proto_library(
-# name = "foo_java_proto",
-# deps = [":foo_proto"],
-# )
-
-go_proto_library(
- name = "foo_go_proto",
- importpath = "example.com/foo_proto",
- proto = ":foo_proto",
+ts_config(
+ name = "tsconfig",
+ src = "tsconfig.json",
)
diff --git a/examples/MODULE.bazel b/examples/MODULE.bazel
index a7180ec..c70cebd 100644
--- a/examples/MODULE.bazel
+++ b/examples/MODULE.bazel
@@ -1,11 +1,15 @@
bazel_dep(name = "toolchains_protoc", version = "0.0.0")
bazel_dep(name = "aspect_bazel_lib", version = "2.11.0")
+bazel_dep(name = "aspect_rules_js", version = "2.2.0")
bazel_dep(name = "aspect_rules_py", version = "1.3.2")
+bazel_dep(name = "aspect_rules_ts", version = "3.5.1")
bazel_dep(name = "platforms", version = "0.0.11")
bazel_dep(name = "protobuf", version = "29.3")
bazel_dep(name = "rules_java", version = "8.6.3")
bazel_dep(name = "rules_proto", version = "7.1.0")
bazel_dep(name = "rules_python", version = "1.2.0-rc0")
+bazel_dep(name = "rules_rust", version = "0.59.1")
+bazel_dep(name = "rules_rust_prost", version = "0.59.1")
bazel_dep(name = "rules_go", version = "0.53.0")
bazel_dep(name = "rules_uv", version = "0.56.0")
@@ -72,3 +76,97 @@
sha256 = "0532ad1024d62361561acaedb974d7d16889e7670b36e23e9321dd6b9d334ef9",
urls = ["https://repo1.maven.org/maven2/com/google/protobuf/protobuf-java/4.27.0-RC3/protobuf-java-4.27.0-RC3.jar"],
)
+
+####### RUST ##########
+RUST_EDITION = "2021"
+
+RUST_VERSION = "1.79.0"
+
+rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
+rust.toolchain(
+ edition = RUST_EDITION,
+ versions = [RUST_VERSION],
+)
+use_repo(rust, "rust_toolchains")
+
+register_toolchains("@rust_toolchains//:all")
+
+# Proto toolchain
+
+crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate")
+
+# protobuf / gRPC dependencies
+crate.spec(
+ package = "prost",
+ version = "0.13.1",
+)
+crate.spec(
+ default_features = False,
+ package = "prost-types",
+ version = "0.13.1",
+)
+crate.spec(
+ features = ["transport"],
+ package = "tonic",
+ version = "0.12.1",
+)
+crate.spec(
+ package = "tonic-build",
+ version = "0.12.1",
+)
+crate.spec(
+ package = "protoc-gen-prost",
+ version = "0.4.0",
+)
+crate.annotation(
+ crate = "protoc-gen-prost",
+ gen_binaries = ["protoc-gen-prost"],
+)
+crate.spec(
+ package = "protoc-gen-tonic",
+ version = "0.4.0",
+)
+crate.annotation(
+ crate = "protoc-gen-tonic",
+ gen_binaries = ["protoc-gen-tonic"],
+)
+crate.spec(
+ default_features = False,
+ features = [
+ "macros",
+ "net",
+ "rt-multi-thread",
+ "signal",
+ ],
+ package = "tokio",
+ version = "1.38",
+)
+crate.from_specs()
+use_repo(crate, "crates")
+
+####### TYPESCRIPT ##########
+npm = use_extension(
+ "@aspect_rules_js//npm:extensions.bzl",
+ "npm",
+ dev_dependency = True,
+)
+
+pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm")
+
+npm.npm_translate_lock(
+ name = "npm",
+ pnpm_lock = "//:pnpm-lock.yaml",
+)
+use_repo(npm, "npm")
+
+use_repo(pnpm, "pnpm")
+
+rules_ts_ext = use_extension(
+ "@aspect_rules_ts//ts:extensions.bzl",
+ "ext",
+ dev_dependency = True,
+)
+rules_ts_ext.deps(
+ ts_version_from = "//:package.json",
+)
+use_repo(rules_ts_ext, "npm_typescript")
diff --git a/examples/WORKSPACE.bazel b/examples/WORKSPACE.bazel
deleted file mode 100644
index e69de29..0000000
--- a/examples/WORKSPACE.bazel
+++ /dev/null
diff --git a/examples/foo.proto b/examples/foo.proto
deleted file mode 100644
index 465255b..0000000
--- a/examples/foo.proto
+++ /dev/null
@@ -1,11 +0,0 @@
-syntax = "proto3";
-
-import "google/protobuf/any.proto";
-
-option go_package = "example.com/foo_proto";
-option java_package = "proto";
-
-message Foo {
- string msg = 1;
- repeated google.protobuf.Any details = 2;
-}
diff --git a/examples/go/BUILD b/examples/go/BUILD
index db2b107..05689b6 100644
--- a/examples/go/BUILD
+++ b/examples/go/BUILD
@@ -1,7 +1,7 @@
load("@rules_go//go:def.bzl", "go_test")
go_test(
- name = "foo_proto_test",
- srcs = ["foo_proto_test.go"],
- deps = ["//:foo_go_proto"],
+ name = "greeter_proto_test",
+ srcs = ["greeter_proto_test.go"],
+ deps = ["//proto:greeter_go_proto"],
)
diff --git a/examples/go/foo_proto_test.go b/examples/go/foo_proto_test.go
deleted file mode 100644
index 114188a..0000000
--- a/examples/go/foo_proto_test.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package proto_test
-
-import (
- "testing"
-
- "example.com/foo_proto"
-)
-
-func TestFoo(t *testing.T) {
- msg := &foo_proto.Foo{
- Msg: "hello world",
- }
- if msg.Msg != "hello world" {
- t.Fail()
- }
-}
diff --git a/examples/go/greeter_proto_test.go b/examples/go/greeter_proto_test.go
new file mode 100644
index 0000000..691d6c0
--- /dev/null
+++ b/examples/go/greeter_proto_test.go
@@ -0,0 +1,16 @@
+package proto_test
+
+import (
+ "testing"
+
+ "example.com/greeter_proto"
+)
+
+func TestFoo(t *testing.T) {
+ msg := &greeter_proto.HelloReply{
+ Message: "hello world",
+ }
+ if msg.Message != "hello world" {
+ t.Fail()
+ }
+}
diff --git a/examples/package.json b/examples/package.json
new file mode 100644
index 0000000..3597763
--- /dev/null
+++ b/examples/package.json
@@ -0,0 +1,15 @@
+{
+ "dependencies": {
+ "@bufbuild/protobuf": "^2.2.4",
+ "@connectrpc/connect-node": "2.0.2",
+ "@connectrpc/connect-fastify": "~2.0.2",
+ "fastify": "~5.2.1"
+ },
+ "devDependencies": {
+ "@bufbuild/protoc-gen-es": "~2.2.4",
+ "@connectrpc/connect": "~2.0.2",
+ "typescript": "5.7.2",
+ "@types/node": "~22.13.10"
+ },
+ "type": "module"
+}
diff --git a/examples/pnpm-lock.yaml b/examples/pnpm-lock.yaml
new file mode 100644
index 0000000..f2b58ab
--- /dev/null
+++ b/examples/pnpm-lock.yaml
@@ -0,0 +1,606 @@
+lockfileVersion: "6.0"
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+dependencies:
+ "@bufbuild/protobuf":
+ specifier: ^2.2.4
+ version: 2.2.4
+ "@connectrpc/connect-fastify":
+ specifier: ~2.0.2
+ version: 2.0.2(@bufbuild/protobuf@2.2.4)(@connectrpc/connect-node@2.0.2)(@connectrpc/connect@2.0.2)(fastify@5.2.1)
+ "@connectrpc/connect-node":
+ specifier: 2.0.2
+ version: 2.0.2(@bufbuild/protobuf@2.2.4)(@connectrpc/connect@2.0.2)
+ fastify:
+ specifier: ~5.2.1
+ version: 5.2.1
+
+devDependencies:
+ "@bufbuild/protoc-gen-es":
+ specifier: ~2.2.4
+ version: 2.2.4(@bufbuild/protobuf@2.2.4)
+ "@connectrpc/connect":
+ specifier: ~2.0.2
+ version: 2.0.2(@bufbuild/protobuf@2.2.4)
+ "@types/node":
+ specifier: ~22.13.10
+ version: 22.13.10
+ typescript:
+ specifier: 5.7.2
+ version: 5.7.2
+
+packages:
+ /@bufbuild/protobuf@2.2.4:
+ resolution:
+ {
+ integrity: sha512-P9xQgtMh71TA7tHTnbDe68zcI+TPnkyyfBIhGaUr4iUEIXN7yI01DyjmmdEwXTk5OlISBJYkoxCVj2dwmHqIkA==,
+ }
+
+ /@bufbuild/protoc-gen-es@2.2.4(@bufbuild/protobuf@2.2.4):
+ resolution:
+ {
+ integrity: sha512-s8hCpjYBCIKTFGfoEdnxIxgZkp3tBtFLz96J82TKNtrOATR5VBj3EPp53/rAG7+I7uguPfcDslO0EZxlzLVtdA==,
+ }
+ engines: { node: ">=14" }
+ hasBin: true
+ peerDependencies:
+ "@bufbuild/protobuf": 2.2.4
+ peerDependenciesMeta:
+ "@bufbuild/protobuf":
+ optional: true
+ dependencies:
+ "@bufbuild/protobuf": 2.2.4
+ "@bufbuild/protoplugin": 2.2.4
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@bufbuild/protoplugin@2.2.4:
+ resolution:
+ {
+ integrity: sha512-OGoc31DokbTu6Ev8808oWPPMgBKLdEBTheMYvRK//2jI7E7oSMxPBlFeRTRiRUfhbQ2TvW20SgJwTLDUsNe8Iw==,
+ }
+ dependencies:
+ "@bufbuild/protobuf": 2.2.4
+ "@typescript/vfs": 1.6.1(typescript@5.4.5)
+ typescript: 5.4.5
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@connectrpc/connect-fastify@2.0.2(@bufbuild/protobuf@2.2.4)(@connectrpc/connect-node@2.0.2)(@connectrpc/connect@2.0.2)(fastify@5.2.1):
+ resolution:
+ {
+ integrity: sha512-rOobWfWc09+Ws4x+tfZFd12UBezMInq+V5chqbgGuGgajZXyEPRfu8ePo0IiKltWptOq7oPAyaEbadpgcZsnBg==,
+ }
+ engines: { node: ">=18.14.1" }
+ peerDependencies:
+ "@bufbuild/protobuf": ^2.2.0
+ "@connectrpc/connect": 2.0.2
+ "@connectrpc/connect-node": 2.0.2
+ fastify: ^4.22.1 || ^5.1.0
+ dependencies:
+ "@bufbuild/protobuf": 2.2.4
+ "@connectrpc/connect": 2.0.2(@bufbuild/protobuf@2.2.4)
+ "@connectrpc/connect-node": 2.0.2(@bufbuild/protobuf@2.2.4)(@connectrpc/connect@2.0.2)
+ fastify: 5.2.1
+ dev: false
+
+ /@connectrpc/connect-node@2.0.2(@bufbuild/protobuf@2.2.4)(@connectrpc/connect@2.0.2):
+ resolution:
+ {
+ integrity: sha512-33Ut3SRkb6SugpwVCtXXRvUrOdtiyG6z6d5+eijBOLOI75sw1tDCwcs0o/9WL3rUj1M08dLUrPmJB47fjpv6EA==,
+ }
+ engines: { node: ">=18.14.1" }
+ peerDependencies:
+ "@bufbuild/protobuf": ^2.2.0
+ "@connectrpc/connect": 2.0.2
+ dependencies:
+ "@bufbuild/protobuf": 2.2.4
+ "@connectrpc/connect": 2.0.2(@bufbuild/protobuf@2.2.4)
+ dev: false
+
+ /@connectrpc/connect@2.0.2(@bufbuild/protobuf@2.2.4):
+ resolution:
+ {
+ integrity: sha512-xZuylIUNvNlH52e/4eQsZvY4QZyDJRtEFEDnn/yBrv5Xi5ZZI/p8X+GAHH35ucVaBvv9u7OzHZo8+tEh1EFTxA==,
+ }
+ peerDependencies:
+ "@bufbuild/protobuf": ^2.2.0
+ dependencies:
+ "@bufbuild/protobuf": 2.2.4
+
+ /@fastify/ajv-compiler@4.0.2:
+ resolution:
+ {
+ integrity: sha512-Rkiu/8wIjpsf46Rr+Fitd3HRP+VsxUFDDeag0hs9L0ksfnwx2g7SPQQTFL0E8Qv+rfXzQOxBJnjUB9ITUDjfWQ==,
+ }
+ dependencies:
+ ajv: 8.17.1
+ ajv-formats: 3.0.1(ajv@8.17.1)
+ fast-uri: 3.0.6
+ dev: false
+
+ /@fastify/error@4.1.0:
+ resolution:
+ {
+ integrity: sha512-KeFcciOr1eo/YvIXHP65S94jfEEqn1RxTRBT1aJaHxY5FK0/GDXYozsQMMWlZoHgi8i0s+YtrLsgj/JkUUjSkQ==,
+ }
+ dev: false
+
+ /@fastify/fast-json-stringify-compiler@5.0.2:
+ resolution:
+ {
+ integrity: sha512-YdR7gqlLg1xZAQa+SX4sMNzQHY5pC54fu9oC5aYSUqBhyn6fkLkrdtKlpVdCNPlwuUuXA1PjFTEmvMF6ZVXVGw==,
+ }
+ dependencies:
+ fast-json-stringify: 6.0.1
+ dev: false
+
+ /@fastify/forwarded@3.0.0:
+ resolution:
+ {
+ integrity: sha512-kJExsp4JCms7ipzg7SJ3y8DwmePaELHxKYtg+tZow+k0znUTf3cb+npgyqm8+ATZOdmfgfydIebPDWM172wfyA==,
+ }
+ dev: false
+
+ /@fastify/merge-json-schemas@0.2.1:
+ resolution:
+ {
+ integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==,
+ }
+ dependencies:
+ dequal: 2.0.3
+ dev: false
+
+ /@fastify/proxy-addr@5.0.0:
+ resolution:
+ {
+ integrity: sha512-37qVVA1qZ5sgH7KpHkkC4z9SK6StIsIcOmpjvMPXNb3vx2GQxhZocogVYbr2PbbeLCQxYIPDok307xEvRZOzGA==,
+ }
+ dependencies:
+ "@fastify/forwarded": 3.0.0
+ ipaddr.js: 2.2.0
+ dev: false
+
+ /@types/node@22.13.10:
+ resolution:
+ {
+ integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==,
+ }
+ dependencies:
+ undici-types: 6.20.0
+ dev: true
+
+ /@typescript/vfs@1.6.1(typescript@5.4.5):
+ resolution:
+ {
+ integrity: sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==,
+ }
+ peerDependencies:
+ typescript: "*"
+ dependencies:
+ debug: 4.4.0
+ typescript: 5.4.5
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /abstract-logging@2.0.1:
+ resolution:
+ {
+ integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==,
+ }
+ dev: false
+
+ /ajv-formats@3.0.1(ajv@8.17.1):
+ resolution:
+ {
+ integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==,
+ }
+ peerDependencies:
+ ajv: ^8.0.0
+ peerDependenciesMeta:
+ ajv:
+ optional: true
+ dependencies:
+ ajv: 8.17.1
+ dev: false
+
+ /ajv@8.17.1:
+ resolution:
+ {
+ integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==,
+ }
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-uri: 3.0.6
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+ dev: false
+
+ /atomic-sleep@1.0.0:
+ resolution:
+ {
+ integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==,
+ }
+ engines: { node: ">=8.0.0" }
+ dev: false
+
+ /avvio@9.1.0:
+ resolution:
+ {
+ integrity: sha512-fYASnYi600CsH/j9EQov7lECAniYiBFiiAtBNuZYLA2leLe9qOvZzqYHFjtIj6gD2VMoMLP14834LFWvr4IfDw==,
+ }
+ dependencies:
+ "@fastify/error": 4.1.0
+ fastq: 1.19.1
+ dev: false
+
+ /cookie@1.0.2:
+ resolution:
+ {
+ integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==,
+ }
+ engines: { node: ">=18" }
+ dev: false
+
+ /debug@4.4.0:
+ resolution:
+ {
+ integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==,
+ }
+ engines: { node: ">=6.0" }
+ peerDependencies:
+ supports-color: "*"
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+ dependencies:
+ ms: 2.1.3
+ dev: true
+
+ /dequal@2.0.3:
+ resolution:
+ {
+ integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==,
+ }
+ engines: { node: ">=6" }
+ dev: false
+
+ /fast-decode-uri-component@1.0.1:
+ resolution:
+ {
+ integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==,
+ }
+ dev: false
+
+ /fast-deep-equal@3.1.3:
+ resolution:
+ {
+ integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==,
+ }
+ dev: false
+
+ /fast-json-stringify@6.0.1:
+ resolution:
+ {
+ integrity: sha512-s7SJE83QKBZwg54dIbD5rCtzOBVD43V1ReWXXYqBgwCwHLYAAT0RQc/FmrQglXqWPpz6omtryJQOau5jI4Nrvg==,
+ }
+ dependencies:
+ "@fastify/merge-json-schemas": 0.2.1
+ ajv: 8.17.1
+ ajv-formats: 3.0.1(ajv@8.17.1)
+ fast-uri: 3.0.6
+ json-schema-ref-resolver: 2.0.1
+ rfdc: 1.4.1
+ dev: false
+
+ /fast-querystring@1.1.2:
+ resolution:
+ {
+ integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==,
+ }
+ dependencies:
+ fast-decode-uri-component: 1.0.1
+ dev: false
+
+ /fast-redact@3.5.0:
+ resolution:
+ {
+ integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==,
+ }
+ engines: { node: ">=6" }
+ dev: false
+
+ /fast-uri@3.0.6:
+ resolution:
+ {
+ integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==,
+ }
+ dev: false
+
+ /fastify@5.2.1:
+ resolution:
+ {
+ integrity: sha512-rslrNBF67eg8/Gyn7P2URV8/6pz8kSAscFL4EThZJ8JBMaXacVdVE4hmUcnPNKERl5o/xTiBSLfdowBRhVF1WA==,
+ }
+ dependencies:
+ "@fastify/ajv-compiler": 4.0.2
+ "@fastify/error": 4.1.0
+ "@fastify/fast-json-stringify-compiler": 5.0.2
+ "@fastify/proxy-addr": 5.0.0
+ abstract-logging: 2.0.1
+ avvio: 9.1.0
+ fast-json-stringify: 6.0.1
+ find-my-way: 9.2.0
+ light-my-request: 6.6.0
+ pino: 9.6.0
+ process-warning: 4.0.1
+ rfdc: 1.4.1
+ secure-json-parse: 3.0.2
+ semver: 7.7.1
+ toad-cache: 3.7.0
+ dev: false
+
+ /fastq@1.19.1:
+ resolution:
+ {
+ integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==,
+ }
+ dependencies:
+ reusify: 1.1.0
+ dev: false
+
+ /find-my-way@9.2.0:
+ resolution:
+ {
+ integrity: sha512-d3uCir8Hmg7W1Ywp8nKf2lJJYU9Nwinvo+1D39Dn09nz65UKXIxUh7j7K8zeWhxqe1WrkS7FJyON/Q/3lPoc6w==,
+ }
+ engines: { node: ">=14" }
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-querystring: 1.1.2
+ safe-regex2: 4.0.1
+ dev: false
+
+ /ipaddr.js@2.2.0:
+ resolution:
+ {
+ integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==,
+ }
+ engines: { node: ">= 10" }
+ dev: false
+
+ /json-schema-ref-resolver@2.0.1:
+ resolution:
+ {
+ integrity: sha512-HG0SIB9X4J8bwbxCbnd5FfPEbcXAJYTi1pBJeP/QPON+w8ovSME8iRG+ElHNxZNX2Qh6eYn1GdzJFS4cDFfx0Q==,
+ }
+ dependencies:
+ dequal: 2.0.3
+ dev: false
+
+ /json-schema-traverse@1.0.0:
+ resolution:
+ {
+ integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==,
+ }
+ dev: false
+
+ /light-my-request@6.6.0:
+ resolution:
+ {
+ integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==,
+ }
+ dependencies:
+ cookie: 1.0.2
+ process-warning: 4.0.1
+ set-cookie-parser: 2.7.1
+ dev: false
+
+ /ms@2.1.3:
+ resolution:
+ {
+ integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==,
+ }
+ dev: true
+
+ /on-exit-leak-free@2.1.2:
+ resolution:
+ {
+ integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==,
+ }
+ engines: { node: ">=14.0.0" }
+ dev: false
+
+ /pino-abstract-transport@2.0.0:
+ resolution:
+ {
+ integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==,
+ }
+ dependencies:
+ split2: 4.2.0
+ dev: false
+
+ /pino-std-serializers@7.0.0:
+ resolution:
+ {
+ integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==,
+ }
+ dev: false
+
+ /pino@9.6.0:
+ resolution:
+ {
+ integrity: sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==,
+ }
+ hasBin: true
+ dependencies:
+ atomic-sleep: 1.0.0
+ fast-redact: 3.5.0
+ on-exit-leak-free: 2.1.2
+ pino-abstract-transport: 2.0.0
+ pino-std-serializers: 7.0.0
+ process-warning: 4.0.1
+ quick-format-unescaped: 4.0.4
+ real-require: 0.2.0
+ safe-stable-stringify: 2.5.0
+ sonic-boom: 4.2.0
+ thread-stream: 3.1.0
+ dev: false
+
+ /process-warning@4.0.1:
+ resolution:
+ {
+ integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==,
+ }
+ dev: false
+
+ /quick-format-unescaped@4.0.4:
+ resolution:
+ {
+ integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==,
+ }
+ dev: false
+
+ /real-require@0.2.0:
+ resolution:
+ {
+ integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==,
+ }
+ engines: { node: ">= 12.13.0" }
+ dev: false
+
+ /require-from-string@2.0.2:
+ resolution:
+ {
+ integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==,
+ }
+ engines: { node: ">=0.10.0" }
+ dev: false
+
+ /ret@0.5.0:
+ resolution:
+ {
+ integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==,
+ }
+ engines: { node: ">=10" }
+ dev: false
+
+ /reusify@1.1.0:
+ resolution:
+ {
+ integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==,
+ }
+ engines: { iojs: ">=1.0.0", node: ">=0.10.0" }
+ dev: false
+
+ /rfdc@1.4.1:
+ resolution:
+ {
+ integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==,
+ }
+ dev: false
+
+ /safe-regex2@4.0.1:
+ resolution:
+ {
+ integrity: sha512-goqsB+bSlOmVX+CiFX2PFc1OV88j5jvBqIM+DgqrucHnUguAUNtiNOs+aTadq2NqsLQ+TQ3UEVG3gtSFcdlkCg==,
+ }
+ dependencies:
+ ret: 0.5.0
+ dev: false
+
+ /safe-stable-stringify@2.5.0:
+ resolution:
+ {
+ integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==,
+ }
+ engines: { node: ">=10" }
+ dev: false
+
+ /secure-json-parse@3.0.2:
+ resolution:
+ {
+ integrity: sha512-H6nS2o8bWfpFEV6U38sOSjS7bTbdgbCGU9wEM6W14P5H0QOsz94KCusifV44GpHDTu2nqZbuDNhTzu+mjDSw1w==,
+ }
+ dev: false
+
+ /semver@7.7.1:
+ resolution:
+ {
+ integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==,
+ }
+ engines: { node: ">=10" }
+ hasBin: true
+ dev: false
+
+ /set-cookie-parser@2.7.1:
+ resolution:
+ {
+ integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==,
+ }
+ dev: false
+
+ /sonic-boom@4.2.0:
+ resolution:
+ {
+ integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==,
+ }
+ dependencies:
+ atomic-sleep: 1.0.0
+ dev: false
+
+ /split2@4.2.0:
+ resolution:
+ {
+ integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==,
+ }
+ engines: { node: ">= 10.x" }
+ dev: false
+
+ /thread-stream@3.1.0:
+ resolution:
+ {
+ integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==,
+ }
+ dependencies:
+ real-require: 0.2.0
+ dev: false
+
+ /toad-cache@3.7.0:
+ resolution:
+ {
+ integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==,
+ }
+ engines: { node: ">=12" }
+ dev: false
+
+ /typescript@5.4.5:
+ resolution:
+ {
+ integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==,
+ }
+ engines: { node: ">=14.17" }
+ hasBin: true
+ dev: true
+
+ /typescript@5.7.2:
+ resolution:
+ {
+ integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==,
+ }
+ engines: { node: ">=14.17" }
+ hasBin: true
+ dev: true
+
+ /undici-types@6.20.0:
+ resolution:
+ {
+ integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==,
+ }
+ dev: true
diff --git a/examples/proto/BUILD.bazel b/examples/proto/BUILD.bazel
new file mode 100644
index 0000000..88c206c
--- /dev/null
+++ b/examples/proto/BUILD.bazel
@@ -0,0 +1,48 @@
+load("@aspect_rules_ts//ts:proto.bzl", "ts_proto_library")
+load("@rules_go//proto:def.bzl", "go_proto_library")
+load("@rules_proto//proto:defs.bzl", "proto_library")
+load("@rules_rust_prost//:defs.bzl", "rust_prost_library")
+load("@protobuf//bazel:py_proto_library.bzl", "py_proto_library")
+
+package(default_visibility = ["//visibility:public"])
+
+proto_library(
+ name = "greeter_proto",
+ srcs = ["greeter.proto"],
+ deps = ["@com_google_protobuf//:any_proto"],
+)
+
+py_proto_library(
+ name = "greeter_py_proto",
+ deps = [":greeter_proto"],
+)
+
+# Broken by https://github.com/protocolbuffers/protobuf/pull/19679
+# which causes building C++ code from source.
+# TODO: re-enable once protobuf honors the toolchain
+# java_proto_library(
+# name = "greeter_java_proto",
+# deps = [":greeter_proto"],
+# )
+
+go_proto_library(
+ name = "greeter_go_proto",
+ importpath = "example.com/greeter_proto",
+ proto = ":greeter_proto",
+)
+
+# Generate Rust bindings from the generated proto files
+# https://bazelbuild.github.io/rules_rust/rust_proto.html#rust_prost_library
+rust_prost_library(
+ name = "greeter_rust_proto",
+ proto = ":greeter_proto",
+ visibility = ["//visibility:public"],
+)
+
+ts_proto_library(
+ name = "greeter_ts_proto",
+ gen_connect_es = False,
+ node_modules = "//:node_modules",
+ proto = ":greeter_proto",
+ proto_srcs = ["greeter.proto"], # to copy greeter.d.ts back to the source tree
+)
diff --git a/examples/proto/greeter.proto b/examples/proto/greeter.proto
new file mode 100644
index 0000000..c2c6d1c
--- /dev/null
+++ b/examples/proto/greeter.proto
@@ -0,0 +1,25 @@
+syntax = "proto3";
+
+// https://github.com/hyperium/tonic/blob/master/examples/proto/helloworld/helloworld.proto
+package proto;
+option go_package = "example.com/greeter_proto";
+option java_package = "proto";
+
+import "google/protobuf/any.proto";
+
+// The greeting service definition.
+service Greeter {
+ // Sends a greeting
+ rpc SayHello(HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+ string name = 1;
+ repeated google.protobuf.Any details = 2;
+}
+
+// The response message containing the greetings
+message HelloReply {
+ string message = 1;
+}
diff --git a/examples/proto/greeter_pb.d.ts b/examples/proto/greeter_pb.d.ts
new file mode 100644
index 0000000..67922cd
--- /dev/null
+++ b/examples/proto/greeter_pb.d.ts
@@ -0,0 +1,64 @@
+// @generated by protoc-gen-es v2.2.4 with parameter "keep_empty_files=true,target=js+dts"
+// @generated from file proto/greeter.proto (package proto, syntax proto3)
+/* eslint-disable */
+
+import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv1";
+import type { Message } from "@bufbuild/protobuf";
+import type { Any } from "@bufbuild/protobuf/wkt";
+
+/**
+ * Describes the file proto/greeter.proto.
+ */
+export declare const file_proto_greeter: GenFile;
+
+/**
+ * @generated from message proto.HelloRequest
+ */
+export declare type HelloRequest = Message<"proto.HelloRequest"> & {
+ /**
+ * @generated from field: string name = 1;
+ */
+ name: string;
+
+ /**
+ * @generated from field: repeated google.protobuf.Any details = 2;
+ */
+ details: Any[];
+};
+
+/**
+ * Describes the message proto.HelloRequest.
+ * Use `create(HelloRequestSchema)` to create a new message.
+ */
+export declare const HelloRequestSchema: GenMessage<HelloRequest>;
+
+/**
+ * @generated from message proto.HelloReply
+ */
+export declare type HelloReply = Message<"proto.HelloReply"> & {
+ /**
+ * @generated from field: string message = 1;
+ */
+ message: string;
+};
+
+/**
+ * Describes the message proto.HelloReply.
+ * Use `create(HelloReplySchema)` to create a new message.
+ */
+export declare const HelloReplySchema: GenMessage<HelloReply>;
+
+/**
+ * @generated from service proto.Greeter
+ */
+export declare const Greeter: GenService<{
+ /**
+ * @generated from rpc proto.Greeter.SayHello
+ */
+ sayHello: {
+ methodKind: "unary";
+ input: typeof HelloRequestSchema;
+ output: typeof HelloReplySchema;
+ },
+}>;
+
diff --git a/examples/proto/src/lib.rs b/examples/proto/src/lib.rs
new file mode 100644
index 0000000..860d77e
--- /dev/null
+++ b/examples/proto/src/lib.rs
@@ -0,0 +1,3 @@
+pub mod proto {
+ tonic::include_proto!("proto");
+}
diff --git a/examples/python/BUILD b/examples/python/BUILD
index 9529879..34becda 100644
--- a/examples/python/BUILD
+++ b/examples/python/BUILD
@@ -3,5 +3,5 @@
py_test(
name = "message_test",
srcs = ["message_test.py"],
- deps = ["//:foo_py_proto"],
+ deps = ["//proto:greeter_py_proto"],
)
diff --git a/examples/python/message_test.py b/examples/python/message_test.py
index 0ea0782..2e03b51 100644
--- a/examples/python/message_test.py
+++ b/examples/python/message_test.py
@@ -1,12 +1,12 @@
import sys
import unittest
-from _main import foo_pb2
+from proto import greeter_pb2
class TestCase(unittest.TestCase):
def test_message(self):
- got = foo_pb2.Foo(
- msg = "hello world",
+ got = greeter_pb2.HelloReply(
+ message = "hello world",
)
self.assertIsNotNone(got)
diff --git a/examples/rust/client/BUILD.bazel b/examples/rust/client/BUILD.bazel
new file mode 100644
index 0000000..08a422c
--- /dev/null
+++ b/examples/rust/client/BUILD.bazel
@@ -0,0 +1,23 @@
+load("@rules_rust//rust:defs.bzl", "rust_binary")
+
+# Build binary
+# https://bazelbuild.github.io/rules_rust/defs.html#rust_binary
+rust_binary(
+ name = "client",
+ srcs = glob([
+ "src/*.rs",
+ ]),
+ crate_root = "src/main.rs",
+ rustc_flags = [
+ "-Copt-level=0",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ # Internal crates
+ "//proto:greeter_rust_proto",
+ # External crates
+ "@crates//:prost-types",
+ "@crates//:tokio",
+ "@crates//:tonic",
+ ],
+)
diff --git a/examples/rust/client/src/main.rs b/examples/rust/client/src/main.rs
new file mode 100644
index 0000000..f4949aa
--- /dev/null
+++ b/examples/rust/client/src/main.rs
@@ -0,0 +1,29 @@
+use greeter_proto::proto::greeter_client::GreeterClient;
+use greeter_proto::proto::HelloRequest;
+use greeter_proto::any_proto::google::protobuf::Any;
+
+// https://github.com/hyperium/tonic/blob/master/examples/src/helloworld/client.rs
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let mut client = GreeterClient::connect("http://[::1]:5042")
+ .await
+ .expect("[Client]: Failed to connect to server.");
+
+ let detail = Any {
+ type_url: "type.googleapis.com/mypackage.MyMessage".to_string(),
+ value: b"details".to_vec(),
+ };
+ let request = tonic::Request::new(HelloRequest {
+ name: "Rust Client".into(),
+ details: vec![detail],
+ });
+
+ let response = client
+ .say_hello(request)
+ .await
+ .expect("[Client]: Failed to get a response from the server");
+
+ println!("RESPONSE={:?}", response);
+
+ Ok(())
+}
diff --git a/examples/rust/server/BUILD.bazel b/examples/rust/server/BUILD.bazel
new file mode 100644
index 0000000..f273a90
--- /dev/null
+++ b/examples/rust/server/BUILD.bazel
@@ -0,0 +1,20 @@
+load("@rules_rust//rust:defs.bzl", "rust_binary")
+
+# Build binary
+# https://bazelbuild.github.io/rules_rust/defs.html#rust_binary
+rust_binary(
+ name = "server",
+ srcs = glob([
+ "src/*.rs",
+ ]),
+ crate_root = "src/main.rs",
+ rustc_flags = ["-Copt-level=0"],
+ visibility = ["//visibility:public"],
+ deps = [
+ # Internal crates
+ "//proto:greeter_rust_proto",
+ # External crates
+ "@crates//:tokio",
+ "@crates//:tonic",
+ ],
+)
diff --git a/examples/rust/server/src/main.rs b/examples/rust/server/src/main.rs
new file mode 100644
index 0000000..7d22bb8
--- /dev/null
+++ b/examples/rust/server/src/main.rs
@@ -0,0 +1,40 @@
+use std::error::Error;
+
+use tonic::transport::Server;
+
+use greeter_proto::proto::greeter_server::GreeterServer;
+
+use crate::server::MyGreeter;
+
+mod server;
+mod shutdown_utils;
+
+// https://github.com/hyperium/tonic/blob/master/examples/src/helloworld/server.rs
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+ let addr = "[::1]:5042"
+ .parse()
+ .expect("[Server]: Failed to parse socket address");
+
+ let grpc_svc = GreeterServer::new(MyGreeter::new());
+
+ // Shutdown signal handler
+ let signal = shutdown_utils::signal_handler("gRPC Greeter server");
+
+ let grpc_server = Server::builder()
+ .add_service(grpc_svc)
+ .serve_with_shutdown(addr, signal);
+
+ let grpc_handle = tokio::spawn(grpc_server);
+
+ println!("GreeterServer listening on {}", addr);
+ match tokio::try_join!(grpc_handle) {
+ Ok(_) => {}
+ Err(e) => {
+ println!("[Server]: Error: Failed to start gRPC Greeter server.");
+ println!("[Server]: Error: {:?}", e);
+ }
+ }
+
+ Ok(())
+}
\ No newline at end of file
diff --git a/examples/rust/server/src/server.rs b/examples/rust/server/src/server.rs
new file mode 100644
index 0000000..4984721
--- /dev/null
+++ b/examples/rust/server/src/server.rs
@@ -0,0 +1,28 @@
+use tonic::{Request, Response, Status};
+
+use greeter_proto::proto::greeter_server::Greeter;
+use greeter_proto::proto::{HelloReply, HelloRequest};
+
+#[derive(Copy, Clone)]
+pub struct MyGreeter {}
+
+impl MyGreeter {
+ pub fn new() -> Self {
+ Self {}
+ }
+}
+
+#[tonic::async_trait]
+impl Greeter for MyGreeter {
+ async fn say_hello(
+ &self,
+ request: Request<HelloRequest>,
+ ) -> Result<Response<HelloReply>, Status> {
+ println!("Got a request from {:?}", request.remote_addr());
+
+ let reply = HelloReply {
+ message: format!("Hello {}!", request.into_inner().name),
+ };
+ Ok(Response::new(reply))
+ }
+}
diff --git a/examples/rust/server/src/shutdown_utils.rs b/examples/rust/server/src/shutdown_utils.rs
new file mode 100644
index 0000000..20d60e1
--- /dev/null
+++ b/examples/rust/server/src/shutdown_utils.rs
@@ -0,0 +1,54 @@
+//
+/// Registers a signal handler that waits for a signal that indicates a shutdown request.
+// https://stackoverflow.com/questions/77585473/rust-tokio-how-to-handle-more-signals-than-just-sigint-i-e-sigquit?noredirect=1#comment136778587_77585473
+pub async fn signal_handler(svc: &str) {
+ wait_for_signal_impl(svc).await
+}
+
+/// Waits for a signal that requests a graceful shutdown. Supports the following signals on unix:
+/// * SIGTERM
+/// * SIGINT (Ctrl-C)
+/// * SIGQUIT
+/// * SIGHUP
+#[cfg(unix)]
+async fn wait_for_signal_impl(svc: &str) {
+ use tokio::signal::unix::{signal, SignalKind};
+
+ // Docs: https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html
+ let mut signal_terminate = signal(SignalKind::terminate()).unwrap();
+ let mut signal_interrupt = signal(SignalKind::interrupt()).unwrap();
+ let mut signal_quit = signal(SignalKind::quit()).unwrap();
+ let mut signal_hang = signal(SignalKind::hangup()).unwrap();
+
+ // https://docs.rs/tokio/latest/tokio/macro.select.html
+ tokio::select! {
+ _ = signal_terminate.recv() => println!("* {svc} received SIGTERM"),
+ _ = signal_interrupt.recv() => println!("* {svc} received SIGINT"),
+ _ = signal_quit.recv() => println!("* {svc} received SIGQUIT"),
+ _ = signal_hang.recv() => println!(" * {svc} received SIGHUP"),
+ }
+}
+
+/// Waits for a signal that requests a graceful shutdown. Supports the following signals on Windows:
+/// * ctrl_c
+/// * ctrl_break
+/// * ctrl_close
+/// * ctrl_shutdown
+#[cfg(windows)]
+async fn wait_for_signal_impl(svc: &str) {
+ use tokio::signal::windows;
+
+ // Docs: https://learn.microsoft.com/en-us/windows/console/handlerroutine
+ let mut signal_c = windows::ctrl_c().unwrap();
+ let mut signal_break = windows::ctrl_break().unwrap();
+ let mut signal_close = windows::ctrl_close().unwrap();
+ let mut signal_shutdown = windows::ctrl_shutdown().unwrap();
+
+ // https://docs.rs/tokio/latest/tokio/macro.select.html
+ tokio::select! {
+ _ = signal_c.recv() => println!("* {svc} received CTRL_C."),
+ _ = signal_break.recv() => println!("* {svc} received CTRL_BREAK."),
+ _ = signal_close.recv() => println!("* {svc} received CTRL_CLOSE."),
+ _ = signal_shutdown.recv() => println!("* {svc} received CTRL_SHUTDOWN."),
+ }
+}
diff --git a/examples/tools/BUILD.bazel b/examples/tools/BUILD.bazel
index 8d490c0..a891879 100644
--- a/examples/tools/BUILD.bazel
+++ b/examples/tools/BUILD.bazel
@@ -1,6 +1,24 @@
load("@platforms//host:constraints.bzl", "HOST_CONSTRAINTS")
+load("@rules_rust//rust:defs.bzl", "rust_library_group")
load("@rules_uv//uv:pip.bzl", "pip_compile")
+package(default_visibility = ["//tools/toolchains:__pkg__"])
+
+rust_library_group(
+ name = "prost_runtime",
+ deps = [
+ "@crates//:prost",
+ ],
+)
+
+rust_library_group(
+ name = "tonic_runtime",
+ deps = [
+ ":prost_runtime",
+ "@crates//:tonic",
+ ],
+)
+
pip_compile(
name = "generate_requirements_txt",
requirements_in = "requirements.in",
diff --git a/examples/tools/toolchains/BUILD.bazel b/examples/tools/toolchains/BUILD.bazel
index 1286682..ccb0c72 100644
--- a/examples/tools/toolchains/BUILD.bazel
+++ b/examples/tools/toolchains/BUILD.bazel
@@ -1,12 +1,5 @@
-"""Define a non-functional cc toolchain.
-
-To fail-fast in cases where we are forced to compile third-party C++ code,
-define a cc toolchain that doesn't work, by using 'false' as the compiler.
-See https://bazel.build/tutorials/ccp-toolchain-config
-"""
-
load("@rules_proto//proto:defs.bzl", "proto_lang_toolchain")
-load("defs.bzl", "cc_toolchain_config")
+load("@rules_rust_prost//:defs.bzl", "rust_prost_toolchain")
# Configure protoc to have the right arguments for generating Python stubs.
# NB: the protobuf team intends to remove --python_out and instead use a protoc plugin for Python stub emit.
@@ -27,29 +20,17 @@
toolchain_type = "@rules_java//java/proto:toolchain_type",
)
-################
-# Setup a non-functional C++ toolchain, so we're assured that no C++ compilation
-# will be expected for engineers working in our repo.
-# That's the critical guarantee of toolchains_protoc
-filegroup(name = "empty")
-
-cc_toolchain_config(name = "noop_toolchain_config")
-
-cc_toolchain(
- name = "noop_toolchain",
- all_files = ":empty",
- compiler_files = ":empty",
- dwp_files = ":empty",
- linker_files = ":empty",
- objcopy_files = ":empty",
- strip_files = ":empty",
- supports_param_files = 0,
- toolchain_config = ":noop_toolchain_config",
- toolchain_identifier = "noop-toolchain",
+rust_prost_toolchain(
+ name = "prost_toolchain_impl",
+ prost_plugin = "@crates//:protoc-gen-prost__protoc-gen-prost",
+ prost_runtime = "//tools:prost_runtime",
+ prost_types = "@crates//:prost-types",
+ tonic_plugin = "@crates//:protoc-gen-tonic__protoc-gen-tonic",
+ tonic_runtime = "//tools:tonic_runtime",
)
toolchain(
- name = "cc_toolchain",
- toolchain = ":noop_toolchain",
- toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+ name = "prost_toolchain",
+ toolchain = "prost_toolchain_impl",
+ toolchain_type = "@rules_rust_prost//:toolchain_type",
)
diff --git a/examples/tools/toolchains/defs.bzl b/examples/tools/toolchains/defs.bzl
deleted file mode 100644
index 4aeee59..0000000
--- a/examples/tools/toolchains/defs.bzl
+++ /dev/null
@@ -1,20 +0,0 @@
-"Configure a cc toolchain to call 'false' if used."
-
-def _impl(ctx):
- return cc_common.create_cc_toolchain_config_info(
- ctx = ctx,
- toolchain_identifier = "noop-toolchain",
- host_system_name = "local",
- target_system_name = "local",
- target_cpu = "k8",
- target_libc = "unknown",
- compiler = "false",
- abi_version = "unknown",
- abi_libc_version = "unknown",
- )
-
-cc_toolchain_config = rule(
- implementation = _impl,
- attrs = {},
- provides = [CcToolchainConfigInfo],
-)
diff --git a/examples/tsconfig.json b/examples/tsconfig.json
new file mode 100644
index 0000000..d5cb4df
--- /dev/null
+++ b/examples/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "compilerOptions": {
+ "lib": ["ES2018"],
+ "module": "ES2022",
+ "target": "ES2017",
+ "moduleResolution": "node"
+ }
+}
diff --git a/examples/typescript/client/BUILD.bazel b/examples/typescript/client/BUILD.bazel
new file mode 100644
index 0000000..464ce97
--- /dev/null
+++ b/examples/typescript/client/BUILD.bazel
@@ -0,0 +1,21 @@
+load("@aspect_rules_js//js:defs.bzl", "js_binary")
+load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
+
+ts_project(
+ name = "main",
+ srcs = ["main.mts"],
+ tsconfig = "//:tsconfig",
+ deps = [
+ "//:node_modules/@connectrpc/connect",
+ "//:node_modules/@connectrpc/connect-node",
+ "//:node_modules/@types/node",
+ "//:package_json",
+ "//proto:greeter_ts_proto",
+ ],
+)
+
+js_binary(
+ name = "client",
+ data = [":main"],
+ entry_point = "main.mjs",
+)
diff --git a/examples/typescript/client/main.mts b/examples/typescript/client/main.mts
new file mode 100644
index 0000000..4c8613c
--- /dev/null
+++ b/examples/typescript/client/main.mts
@@ -0,0 +1,21 @@
+import { createClient , CallOptions} from "@connectrpc/connect";
+import { createGrpcTransport } from "@connectrpc/connect-node";
+import {Greeter} from "../../proto/greeter_pb.js";
+
+const transport = createGrpcTransport({
+ baseUrl: "http://localhost:5042",
+});
+
+async function main() {
+ const client = createClient(Greeter, transport);
+ const res = await client.sayHello({
+ name: "TypeScript Client",
+ }, {onHeader: (headers) => {
+ console.log("Headers:")
+ for (const [name, value] of headers) {
+ console.log(`${name}: ${value}`);
+ }
+ }});
+ console.log((res as /*todo*/any).message);
+}
+void main();
diff --git a/examples/typescript/server/BUILD.bazel b/examples/typescript/server/BUILD.bazel
new file mode 100644
index 0000000..99559a5
--- /dev/null
+++ b/examples/typescript/server/BUILD.bazel
@@ -0,0 +1,25 @@
+load("@aspect_rules_js//js:defs.bzl", "js_binary")
+load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
+
+ts_project(
+ name = "main",
+ srcs = [
+ "connect.mts",
+ "main.mts",
+ ],
+ tsconfig = "//:tsconfig",
+ deps = [
+ "//:node_modules/@connectrpc/connect",
+ "//:node_modules/@connectrpc/connect-fastify",
+ "//:node_modules/@types/node",
+ "//:node_modules/fastify",
+ "//:package_json",
+ "//proto:greeter_ts_proto",
+ ],
+)
+
+js_binary(
+ name = "server",
+ data = [":main"],
+ entry_point = "main.mjs",
+)
diff --git a/examples/typescript/server/connect.mts b/examples/typescript/server/connect.mts
new file mode 100644
index 0000000..23e63eb
--- /dev/null
+++ b/examples/typescript/server/connect.mts
@@ -0,0 +1,11 @@
+import { ConnectRouter, HandlerContext } from "@connectrpc/connect";
+import { Greeter, HelloRequest } from "../../proto/greeter_pb.js";
+
+export default (router: ConnectRouter) =>
+ router.service(Greeter, {
+ async sayHello(req: HelloRequest, context: HandlerContext) {
+ return {
+ message: `Hello ${req.name}!`,
+ };
+ }
+ });
diff --git a/examples/typescript/server/main.mts b/examples/typescript/server/main.mts
new file mode 100644
index 0000000..ecf9c0f
--- /dev/null
+++ b/examples/typescript/server/main.mts
@@ -0,0 +1,19 @@
+import { fastify } from "fastify";
+import { fastifyConnectPlugin } from "@connectrpc/connect-fastify";
+import routes from "./connect.mjs";
+import { readFileSync } from "fs";
+
+// See https://connectrpc.com/docs/node/getting-started
+async function main() {
+ const server = fastify({http2: true});
+ await server.register(fastifyConnectPlugin, {routes});
+ server.get("/", (_, reply) => {
+ reply.type("text/plain");
+ reply.send("Hello World!");
+ });
+ await server.listen({ host: "localhost", port: 5042 });
+ console.log("server is listening at", server.addresses());
+}
+// You can remove the main() wrapper if you set type: module in your package.json,
+// and update your tsconfig.json with target: es2017 and module: es2022.
+void main();