Protocol Buffers for JavaScript with TypeScript support, providing pure JavaScript implementation for serializing and deserializing structured data using Google's Protocol Buffer format.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Built-in RPC service framework for implementing and consuming protobuf-based services with support for both traditional and streaming RPC patterns.
Service reflection and definition management for RPC services.
class Service extends Namespace {
/**
* Constructs a new service
* @param name - Service name
* @param options - Service options
*/
constructor(name: string, options?: object);
/**
* Creates service from JSON descriptor
* @param name - Service name
* @param json - JSON service descriptor
* @returns Service instance
*/
static fromJSON(name: string, json: object): Service;
/**
* Service methods by name
*/
methods: { [name: string]: Method };
/**
* Array of all methods
*/
methodsArray: Method[];
/**
* Creates RPC service instance
* @param rpcImpl - RPC implementation function
* @param requestDelimited - Whether requests are length-delimited
* @param responseDelimited - Whether responses are length-delimited
* @returns RPC service instance
*/
create(rpcImpl: RPCImpl, requestDelimited?: boolean, responseDelimited?: boolean): rpc.Service;
/**
* Adds method to service
* @param method - Method to add
* @returns This service
*/
add(method: Method): Service;
/**
* Removes method from service
* @param method - Method to remove
* @returns This service
*/
remove(method: Method): Service;
}Usage Examples:
const protobuf = require("protobufjs");
protobuf.load("service.proto", function(err, root) {
const UserService = root.lookupService("UserService");
console.log("Service methods:");
UserService.methodsArray.forEach(method => {
console.log(`${method.name}: ${method.requestType} -> ${method.responseType}`);
});
// Create service instance
const service = UserService.create(rpcImplementation);
});RPC method reflection and metadata.
class Method extends ReflectionObject {
/**
* Constructs a new method
* @param name - Method name
* @param type - Method type (typically "rpc")
* @param requestType - Request message type name
* @param responseType - Response message type name
* @param requestStream - Whether request is streaming
* @param responseStream - Whether response is streaming
* @param options - Method options
*/
constructor(name: string, type: string, requestType: string, responseType: string,
requestStream?: boolean, responseStream?: boolean, options?: object);
/**
* Method type (typically "rpc")
*/
type: string;
/**
* Request message type name
*/
requestType: string;
/**
* Response message type name
*/
responseType: string;
/**
* Whether request is streaming
*/
requestStream: boolean;
/**
* Whether response is streaming
*/
responseStream: boolean;
/**
* Resolved request message type
*/
resolvedRequestType: Type | null;
/**
* Resolved response message type
*/
resolvedResponseType: Type | null;
}Usage Examples:
// Inspect method details
const getUserMethod = UserService.methods["getUser"];
console.log("Method:", getUserMethod.name);
console.log("Request type:", getUserMethod.requestType);
console.log("Response type:", getUserMethod.responseType);
console.log("Streaming:", {
request: getUserMethod.requestStream,
response: getUserMethod.responseStream
});
// Access resolved types
const RequestType = getUserMethod.resolvedRequestType;
const ResponseType = getUserMethod.resolvedResponseType;Interface for implementing RPC transport and execution.
/**
* RPC implementation function
* @param method - Method being called
* @param requestData - Encoded request data
* @param callback - Callback for response
*/
interface RPCImpl {
(method: Method, requestData: Uint8Array, callback: RPCImplCallback): void;
}
/**
* RPC implementation callback
* @param error - Error if call failed, null on success
* @param response - Encoded response data if successful
*/
interface RPCImplCallback {
(error: Error | null, response?: Uint8Array): void;
}Usage Examples:
// HTTP-based RPC implementation
function httpRpcImpl(method, requestData, callback) {
const xhr = new XMLHttpRequest();
xhr.open("POST", `/rpc/${method.name}`);
xhr.responseType = "arraybuffer";
xhr.onload = function() {
if (xhr.status === 200) {
const responseBuffer = new Uint8Array(xhr.response);
callback(null, responseBuffer);
} else {
callback(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`));
}
};
xhr.onerror = function() {
callback(new Error("Network error"));
};
xhr.send(requestData);
}
// WebSocket-based RPC implementation
function websocketRpcImpl(method, requestData, callback) {
const message = {
method: method.name,
data: Array.from(requestData)
};
websocket.send(JSON.stringify(message));
// Store callback for response matching
pendingCalls[generateId()] = callback;
}
// Node.js HTTP client implementation
function nodeHttpRpcImpl(method, requestData, callback) {
const options = {
hostname: 'localhost',
port: 8080,
path: `/rpc/${method.name}`,
method: 'POST',
headers: {
'Content-Type': 'application/x-protobuf',
'Content-Length': requestData.length
}
};
const req = require('http').request(options, (res) => {
const chunks = [];
res.on('data', chunk => chunks.push(chunk));
res.on('end', () => {
const responseBuffer = Buffer.concat(chunks);
callback(null, new Uint8Array(responseBuffer));
});
});
req.on('error', callback);
req.write(requestData);
req.end();
}Runtime service instance for making RPC calls.
namespace rpc {
class Service {
/**
* RPC implementation
*/
rpcImpl: RPCImpl;
/**
* Whether requests are length-delimited
*/
requestDelimited: boolean;
/**
* Whether responses are length-delimited
*/
responseDelimited: boolean;
/**
* Creates service method wrapper
* @param method - Method definition
* @param requestCtor - Request message constructor
* @param responseCtor - Response message constructor
* @returns Method wrapper function
*/
rpcCall<TRequest, TResponse>(
method: Method,
requestCtor: Constructor<TRequest>,
responseCtor: Constructor<TResponse>
): (request: TRequest, callback: ServiceMethodCallback<TResponse>) => void;
}
}
/**
* Service method callback
* @param error - Error if call failed
* @param response - Response message if successful
*/
interface ServiceMethodCallback<TResponse> {
(error: Error | null, response?: TResponse): void;
}Usage Examples:
// Create and use service instance
const userService = UserService.create(httpRpcImpl);
// Make RPC calls
const getUserRequest = { userId: 123 };
userService.getUser(getUserRequest, function(err, response) {
if (err) {
console.error("RPC error:", err);
return;
}
console.log("User:", response.name, response.email);
});
// Promise-based wrapper
function promisifyService(service) {
const promisified = {};
Object.keys(service).forEach(methodName => {
if (typeof service[methodName] === 'function') {
promisified[methodName] = function(request) {
return new Promise((resolve, reject) => {
service[methodName](request, (err, response) => {
if (err) reject(err);
else resolve(response);
});
});
};
}
});
return promisified;
}
// Use promisified service
const promisedUserService = promisifyService(userService);
promisedUserService.getUser({ userId: 123 })
.then(user => console.log("User:", user))
.catch(err => console.error("Error:", err));Support for streaming RPC patterns (client streaming, server streaming, bidirectional streaming).
interface StreamingPatterns {
/**
* Unary RPC (single request, single response)
*/
unary: {
requestStream: false;
responseStream: false;
};
/**
* Client streaming (multiple requests, single response)
*/
clientStreaming: {
requestStream: true;
responseStream: false;
};
/**
* Server streaming (single request, multiple responses)
*/
serverStreaming: {
requestStream: false;
responseStream: true;
};
/**
* Bidirectional streaming (multiple requests, multiple responses)
*/
bidirectionalStreaming: {
requestStream: true;
responseStream: true;
};
}Usage Examples:
// Server streaming implementation
function serverStreamingRpcImpl(method, requestData, callback) {
if (method.responseStream) {
// Set up streaming response handler
const stream = new EventEmitter();
stream.on('data', (responseData) => {
// Each response chunk
callback(null, responseData);
});
stream.on('end', () => {
// Signal end of stream
callback(null, null);
});
stream.on('error', (err) => {
callback(err);
});
// Initiate streaming call
initiateStreamingCall(method.name, requestData, stream);
} else {
// Regular unary call
regularRpcImpl(method, requestData, callback);
}
}
// Client streaming implementation
function clientStreamingService(method, requestStream) {
return new Promise((resolve, reject) => {
const chunks = [];
requestStream.on('data', (requestData) => {
chunks.push(requestData);
});
requestStream.on('end', () => {
// Combine all request chunks
const combinedRequest = Buffer.concat(chunks);
// Make single RPC call with combined data
rpcImpl(method, combinedRequest, (err, response) => {
if (err) reject(err);
else resolve(response);
});
});
});
}Utilities for registering and discovering RPC services.
interface ServiceRegistry {
/**
* Registers service implementation
* @param serviceName - Service name
* @param implementation - Service implementation
*/
register(serviceName: string, implementation: object): void;
/**
* Gets registered service
* @param serviceName - Service name
* @returns Service implementation
*/
get(serviceName: string): object | null;
/**
* Lists all registered services
* @returns Array of service names
*/
list(): string[];
}Usage Examples:
// Service implementation
const userServiceImpl = {
getUser: function(request, callback) {
// Implementation logic
const user = database.findUser(request.userId);
const response = UserResponse.create({
id: user.id,
name: user.name,
email: user.email
});
callback(null, response);
},
createUser: function(request, callback) {
// Implementation logic
const newUser = database.createUser(request);
const response = UserResponse.create(newUser);
callback(null, response);
}
};
// Register service
serviceRegistry.register("UserService", userServiceImpl);
// Generic RPC handler
function handleRpcCall(serviceName, methodName, requestData, callback) {
const service = serviceRegistry.get(serviceName);
if (!service) {
return callback(new Error(`Service not found: ${serviceName}`));
}
const method = service[methodName];
if (!method) {
return callback(new Error(`Method not found: ${methodName}`));
}
// Decode request and call method
const ServiceDef = root.lookupService(serviceName);
const MethodDef = ServiceDef.methods[methodName];
const RequestType = MethodDef.resolvedRequestType;
const request = RequestType.decode(requestData);
method(request, callback);
}Comprehensive error handling for RPC operations.
interface RPCError extends Error {
name: string; // Error type
message: string; // Error description
code?: number; // Error code
details?: any; // Additional error details
method?: string; // Method that failed
}
interface RPCErrorCodes {
OK: 0;
CANCELLED: 1;
UNKNOWN: 2;
INVALID_ARGUMENT: 3;
DEADLINE_EXCEEDED: 4;
NOT_FOUND: 5;
ALREADY_EXISTS: 6;
PERMISSION_DENIED: 7;
UNAUTHENTICATED: 16;
RESOURCE_EXHAUSTED: 8;
FAILED_PRECONDITION: 9;
ABORTED: 10;
OUT_OF_RANGE: 11;
UNIMPLEMENTED: 12;
INTERNAL: 13;
UNAVAILABLE: 14;
DATA_LOSS: 15;
}Usage Examples:
// Enhanced error handling in RPC implementation
function robustRpcImpl(method, requestData, callback) {
try {
// Validate method
if (!method) {
const error = new Error("Method not specified");
error.code = RPCErrorCodes.INVALID_ARGUMENT;
return callback(error);
}
// Make RPC call with timeout
const timeoutId = setTimeout(() => {
const error = new Error("RPC call timed out");
error.code = RPCErrorCodes.DEADLINE_EXCEEDED;
error.method = method.name;
callback(error);
}, 30000);
makeRpcCall(method, requestData, (err, response) => {
clearTimeout(timeoutId);
if (err) {
// Enhance error with context
err.method = method.name;
err.code = err.code || RPCErrorCodes.UNKNOWN;
}
callback(err, response);
});
} catch (err) {
err.code = RPCErrorCodes.INTERNAL;
err.method = method.name;
callback(err);
}
}
// Error handling in service calls
userService.getUser(request, function(err, response) {
if (err) {
switch (err.code) {
case RPCErrorCodes.NOT_FOUND:
console.log("User not found");
break;
case RPCErrorCodes.PERMISSION_DENIED:
console.log("Access denied");
break;
case RPCErrorCodes.UNAVAILABLE:
console.log("Service unavailable, retrying...");
// Implement retry logic
break;
default:
console.error("RPC error:", err.message);
}
return;
}
// Success
console.log("User retrieved:", response);
});interface RPCImpl {
(method: Method, requestData: Uint8Array, callback: RPCImplCallback): void;
}
interface RPCImplCallback {
(error: Error | null, response?: Uint8Array): void;
}
interface ServiceMethodCallback<TResponse> {
(error: Error | null, response?: TResponse): void;
}
interface Constructor<T> extends Function {
new (...args: any[]): T;
}Install with Tessl CLI
npx tessl i tessl/npm-protobufjs