Skip to content

Instantly share code, notes, and snippets.

@pojntfx
Created May 26, 2024 21:29
Show Gist options
  • Save pojntfx/ae622cf34c360bb9b3ad955395372362 to your computer and use it in GitHub Desktop.
Save pojntfx/ae622cf34c360bb9b3ad955395372362 to your computer and use it in GitHub Desktop.
Use `panrpc` to connect a GJS application to a Go/NodeJS/Bun backend with bi-directional RPCs
const root =
(typeof globalThis !== "undefined" && globalThis) ||
(typeof self !== "undefined" && self) ||
(typeof global !== "undefined" && global);
function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = new Event(event, {
bubbles: params.bubbles,
cancelable: params.cancelable,
});
evt.detail = params.detail
return evt;
}
CustomEvent.prototype = root.Event.prototype;
root.CustomEvent = CustomEvent;
import Glib from "gi://GLib?version=2.0";
const root =
(typeof globalThis !== "undefined" && globalThis) ||
(typeof self !== "undefined" && self) ||
(typeof global !== "undefined" && global);
root.crypto = {
// THIS IS UNSAFE, GLIB DOES _NOT_ GUARANTEE CRYPTOGRAPHIC SAFETY FOR THESE UUIDS, DO _NOT_ USE THIS IN PRODUCTION
randomUUID: Glib.uuid_string_random,
};
dev:
npx esbuild playground.js --bundle --outfile=/tmp/playground.js '--external:gi://*' --platform=neutral && gjs -m /tmp/playground.js
{
"dependencies": {
"@pojntfx/panrpc": "^0.8.1",
"@streamparser/json": "^0.0.21",
"@streamparser/json-whatwg": "^0.0.21",
"esbuild": "^0.21.4",
"event-target-polyfill": "^0.0.4",
"troll": "github:sonnyp/troll",
"web-streams-polyfill": "^4.0.0"
}
}
import "web-streams-polyfill/polyfill";
import "event-target-polyfill";
import "./gjs-crypto-polyfill.js";
import "./custom-event-polyfill.js";
import { JSONParser } from "@streamparser/json-whatwg";
import { Registry } from "@pojntfx/panrpc";
import WebSocket from "troll/src/std/WebSocket.js";
// Use `npx tsx ./bin/panrpc-example-websocket-server-cli.ts`, `bun run ./bin/panrpc-example-websocket-server-cli.ts` or `go run ./cmd/panrpc-example-websocket-server-cli/` to start the Go/NodeJS/Bun server
// See https://github.com/pojntfx/panrpc for more information on the RPC framework and how to use closures etc.
class Local {
async Println(ctx, msg) {
console.log("Printing message", msg, "for remote with ID", ctx.remoteID);
console.log(msg);
}
}
class Remote {
async Increment(ctx, delta) {
return 0;
}
}
let clients = 0;
const registry = new Registry(
new Local(),
new Remote(),
{
onClientConnect: () => {
clients++;
console.log(clients, "clients connected");
},
onClientDisconnect: () => {
clients--;
console.log(clients, "clients connected");
},
}
);
const addr = "127.0.0.1:1337";
const socket = new WebSocket(`ws://${addr}/`); // Note the trailing `/` - Soup's WebSocket implementation doesn't connect otherwise!
socket.addEventListener("error", (e) => {
console.error("Disconnected with error:", e);
});
socket.addEventListener("close", () => {
console.error("Exiting ...");
});
await new Promise((res, rej) => {
socket.addEventListener("open", () => res());
socket.addEventListener("error", rej);
});
const encoder = new WritableStream({
write(chunk) {
socket.send(JSON.stringify(chunk));
},
});
const parser = new JSONParser({
paths: ["$"],
separator: "",
});
const parserWriter = parser.writable.getWriter();
const parserReader = parser.readable.getReader();
const decoder = new ReadableStream({
start(controller) {
parserReader
.read()
.then(async function process({ done, value }) {
if (done) {
controller.close();
return;
}
controller.enqueue(value?.value);
parserReader
.read()
.then(process)
.catch((e) => controller.error(e));
})
.catch((e) => controller.error(e));
},
});
socket.addEventListener("message", (m) => parserWriter.write(m.data));
socket.addEventListener("close", () => {
parserReader.cancel();
parserWriter.abort();
});
registry.linkStream(
encoder,
decoder,
(v) => v,
(v) => v
);
console.log("Connected to", addr);
await registry.forRemotes(async (remoteID, remote) => {
console.log("Calling functions for remote with ID", remoteID);
try {
const res = await remote.Increment(undefined, 1);
console.log(res);
} catch (e) {
console.error(`Got error for Increment func: ${e}`);
}
try {
const res = await remote.Increment(undefined, -1);
console.log(res);
} catch (e) {
console.error(`Got error for Increment func: ${e}`);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment