diff --git a/.gitignore b/.gitignore index d09eed8950..ba34d50c8a 100644 --- a/.gitignore +++ b/.gitignore @@ -78,7 +78,7 @@ cardano-wasm/grpc-example/cardano-wasm.wasm cardano-wasm/grpc-example/cardano-wasm.js cardano-wasm/grpc-example/cardano-api.d.ts cardano-wasm/grpc-example/cardano-api.js -cardano-wasm/grpc-example/node_grpc_web_pb.js +cardano-wasm/grpc-example/cardano_node_grpc_web_pb.js cardano-wasm/example/cardano-wasm.wasm cardano-wasm/example/cardano-wasm.js cardano-wasm/example/cardano-api.d.ts diff --git a/cardano-wasm/README.md b/cardano-wasm/README.md index abf53f35f8..798ba18407 100644 --- a/cardano-wasm/README.md +++ b/cardano-wasm/README.md @@ -239,19 +239,8 @@ To run the example in the `example` subfolder: To run the example in the `grpc-example` subfolder: 1. Run an instance of the `cardano-node` with the GRPC server enabled and put the socket file for the GRPC server in the root folder of this repo with the name `rpc.socket`. (You can put it somewhere else, but you will have to update the `envoy-conf.yaml` function later.) -2. Generate the JS GRPC client bundle `node_grpc_web_pb.js` from the GRPC server proto files by either: - - Running `nix build .#proto-js-bundle`. (This will generate it under the `result` folder.) - - Or using `protoc`, `npm` and `browserify`: - ``` - protoc -I../../cardano-rpc/proto --js_out=import_style=commonjs,binary:./ --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. ../../cardano-rpc/proto/cardano/rpc/node.proto - - npm install grpc-web - - npm install google-protobuf - - browserify --standalone grpc cardano/rpc/node_grpc_web_pb.js > node_grpc_web_pb.js - ``` -3. Copy the generated `node_grpc_web_pb.js` file to the `grpc-example` subfolder. +2. Generate the JS GRPC client bundle `cardano_node_grpc_web_pb.js` from the GRPC server proto files by running `nix build .#proto-js-bundle`. (This will generate it under the `result` folder.) +3. Copy the generated `cardano_node_grpc_web_pb.js` file to the `grpc-example` subfolder. 4. Copy the generated `cardano-wasm.wasm` file to the `grpc-example` subfolder. You can find its location using: ```console echo "$(env -u CABAL_CONFIG wasm32-wasi-cabal list-bin exe:cardano-wasm | tail -n1)" diff --git a/cardano-wasm/grpc-example/envoy-conf.yaml b/cardano-wasm/grpc-example/envoy-conf.yaml index 38a3efe700..c2702b8874 100644 --- a/cardano-wasm/grpc-example/envoy-conf.yaml +++ b/cardano-wasm/grpc-example/envoy-conf.yaml @@ -93,7 +93,7 @@ static_resources: body: filename: "./example.js" - match: - path: "/node_grpc_web_pb.js" + path: "/cardano_node_grpc_web_pb.js" response_headers_to_add: - header: key: "Content-Type" @@ -101,7 +101,7 @@ static_resources: direct_response: status: 200 body: - filename: "./node_grpc_web_pb.js" + filename: "./cardano_node_grpc_web_pb.js" http_filters: - name: envoy.filters.http.grpc_web typed_config: diff --git a/cardano-wasm/grpc-example/index.html b/cardano-wasm/grpc-example/index.html index ec13bf9915..fc83897fd6 100644 --- a/cardano-wasm/grpc-example/index.html +++ b/cardano-wasm/grpc-example/index.html @@ -3,7 +3,7 @@ cardano-wasm test - +

Test output

diff --git a/cardano-wasm/src/Cardano/Wasm/Internal/JavaScript/GRPC.hs b/cardano-wasm/src/Cardano/Wasm/Internal/JavaScript/GRPC.hs index a1445683a7..14b9bcb595 100644 --- a/cardano-wasm/src/Cardano/Wasm/Internal/JavaScript/GRPC.hs +++ b/cardano-wasm/src/Cardano/Wasm/Internal/JavaScript/GRPC.hs @@ -9,14 +9,16 @@ module Cardano.Wasm.Internal.JavaScript.GRPC (js_newWebGrpcClient, js_getEra) wh import GHC.Wasm.Prim -- | Create a GRPC-web client for the Cardano API. -foreign import javascript safe "new grpc.NodePromiseClient($1, null, null)" +foreign import javascript safe "{ node: new cardano_node.node.NodePromiseClient($1, null, null), \ + query: new cardano_node.query.QueryServicePromiseClient($1, null, null) \ + }" js_newWebGrpcClientImpl :: JSString -> IO JSVal js_newWebGrpcClient :: String -> IO JSVal js_newWebGrpcClient = js_newWebGrpcClientImpl . toJSString -- | Get the era from the Cardano API using a GRPC-web client. -foreign import javascript safe "($1).getEra(new proto.Empty(), {})" +foreign import javascript safe "($1).node.getEra(new proto.Empty(), {})" js_getEra :: JSVal -> IO Int #endif diff --git a/nix/proto-to-js.nix b/nix/proto-to-js.nix index e1f7c1efff..b990b87650 100644 --- a/nix/proto-to-js.nix +++ b/nix/proto-to-js.nix @@ -43,8 +43,9 @@ in pkgs.stdenv.mkDerivation { mkdir -p "$GEN_JS_PATH" mkdir -p "$BUNDLE_PATH" - echo "--- Compiling .proto file: $PROTO_FILE ---" + echo "--- Compiling .proto files in $PROTO_INCLUDE_PATH ---" + # Find all .proto files and compile them. for PROTO_FILE in `find "$PROTO_INCLUDE_PATH" -type f -name "*.proto"` do protoc \ @@ -57,32 +58,55 @@ in pkgs.stdenv.mkDerivation { echo "--- Compilation finished. Generated files are in $GEN_JS_PATH ---" ls -R "$GEN_JS_PATH" - # Check if there are any files in the top-level generated directory - if [ ! "$(ls -1 "$GEN_JS_PATH" | head -n 1)" ]; then - echo "Error: protoc did not generate any gRPC-Web files!" - exit 1 - fi + # Generate JS file that imports the generated files for Browserify + ENTRYPOINT_FILE=$GEN_JS_PATH/index.js + echo "--- Creating browserify entrypoint: $ENTRYPOINT_FILE ---" + + # Ensure the entrypoint file is empty before we start. + rm -f $ENTRYPOINT_FILE + touch "$ENTRYPOINT_FILE" + + # Find all *_grpc_web_pb.js files and build the entrypoint content. + for JS_FULLPATH in `find "$GEN_JS_PATH" -type f -name "*_grpc_web_pb.js"` + do + # Get the filename, e.g., "node_grpc_web_pb.js" + JS_FILENAME=$(basename "$JS_FULLPATH") + + # Extract the module name by removing the suffix + MODULE_NAME=''${JS_FILENAME%_grpc_web_pb.js} + + # Get the path relative to GEN_JS_PATH for the require() statement. + RELATIVE_PATH=''${JS_FULLPATH#$GEN_JS_PATH/} + + echo "Adding module '$MODULE_NAME' from './$RELATIVE_PATH' to bundle." + # Append the export line to our entrypoint file. + # This creates the desired submodule structure. + echo "exports.$MODULE_NAME = require('./$RELATIVE_PATH');" >> "$ENTRYPOINT_FILE" + done + + echo "--- Generated entrypoint content: ---" + cat "$ENTRYPOINT_FILE" echo "--- Setting up node_modules for browserify ---" ln -s ${node-deps}/node_modules ./node_modules - echo "--- Bundling generated JS with browserify ---" + echo "--- Bundling all generated JS gRPC modules with browserify ---" - for GENERATED_GRPC_FILE in `find "$GEN_JS_PATH" -type f -name "*.js"` - do - browserify --standalone grpc "$GENERATED_GRPC_FILE" > "$BUNDLE_PATH/$(basename $GENERATED_GRPC_FILE)" - done + # Bundle the entrypoint file into a single standalone module. + # The --standalone flag exposes the exports under the 'cardano_node' global variable. + browserify "$ENTRYPOINT_FILE" --standalone cardano_node > "$BUNDLE_PATH/cardano_node_grpc_web_pb.js" - echo "--- Bundling complete. Final files are in $BUNDLE_PATH ---" - ls "$BUNDLE_PATH" + echo "--- Bundling complete. Final file is in $BUNDLE_PATH ---" + ls -l "$BUNDLE_PATH" runHook postBuild ''; installPhase = '' runHook preInstall - mkdir -p "$out" - cp ./bundled-js/*_grpc_web_pb.js "$out/" + mkdir -p $out + # Copy the final, correctly named bundle to the output directory. + cp ./bundled-js/cardano_node_grpc_web_pb.js $out/ runHook postInstall '';