initial commit of actions

This commit is contained in:
Dominik Polakovics Polakovics 2026-01-31 18:56:04 +01:00
commit 949ece5785
44660 changed files with 12034344 additions and 0 deletions

View file

@ -0,0 +1,50 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClientStreamingCall = void 0;
/**
* A client streaming RPC call. This means that the clients sends 0, 1, or
* more messages to the server, and the server replies with exactly one
* message.
*/
class ClientStreamingCall {
constructor(method, requestHeaders, request, headers, response, status, trailers) {
this.method = method;
this.requestHeaders = requestHeaders;
this.requests = request;
this.headers = headers;
this.response = response;
this.status = status;
this.trailers = trailers;
}
/**
* Instead of awaiting the response status and trailers, you can
* just as well await this call itself to receive the server outcome.
* Note that it may still be valid to send more request messages.
*/
then(onfulfilled, onrejected) {
return this.promiseFinished().then(value => onfulfilled ? Promise.resolve(onfulfilled(value)) : value, reason => onrejected ? Promise.resolve(onrejected(reason)) : Promise.reject(reason));
}
promiseFinished() {
return __awaiter(this, void 0, void 0, function* () {
let [headers, response, status, trailers] = yield Promise.all([this.headers, this.response, this.status, this.trailers]);
return {
method: this.method,
requestHeaders: this.requestHeaders,
headers,
response,
status,
trailers
};
});
}
}
exports.ClientStreamingCall = ClientStreamingCall;

View file

@ -0,0 +1,86 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Deferred = exports.DeferredState = void 0;
var DeferredState;
(function (DeferredState) {
DeferredState[DeferredState["PENDING"] = 0] = "PENDING";
DeferredState[DeferredState["REJECTED"] = 1] = "REJECTED";
DeferredState[DeferredState["RESOLVED"] = 2] = "RESOLVED";
})(DeferredState = exports.DeferredState || (exports.DeferredState = {}));
/**
* A deferred promise. This is a "controller" for a promise, which lets you
* pass a promise around and reject or resolve it from the outside.
*
* Warning: This class is to be used with care. Using it can make code very
* difficult to read. It is intended for use in library code that exposes
* promises, not for regular business logic.
*/
class Deferred {
/**
* @param preventUnhandledRejectionWarning - prevents the warning
* "Unhandled Promise rejection" by adding a noop rejection handler.
* Working with calls returned from the runtime-rpc package in an
* async function usually means awaiting one call property after
* the other. This means that the "status" is not being awaited when
* an earlier await for the "headers" is rejected. This causes the
* "unhandled promise reject" warning. A more correct behaviour for
* calls might be to become aware whether at least one of the
* promises is handled and swallow the rejection warning for the
* others.
*/
constructor(preventUnhandledRejectionWarning = true) {
this._state = DeferredState.PENDING;
this._promise = new Promise((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
});
if (preventUnhandledRejectionWarning) {
this._promise.catch(_ => { });
}
}
/**
* Get the current state of the promise.
*/
get state() {
return this._state;
}
/**
* Get the deferred promise.
*/
get promise() {
return this._promise;
}
/**
* Resolve the promise. Throws if the promise is already resolved or rejected.
*/
resolve(value) {
if (this.state !== DeferredState.PENDING)
throw new Error(`cannot resolve ${DeferredState[this.state].toLowerCase()}`);
this._resolve(value);
this._state = DeferredState.RESOLVED;
}
/**
* Reject the promise. Throws if the promise is already resolved or rejected.
*/
reject(reason) {
if (this.state !== DeferredState.PENDING)
throw new Error(`cannot reject ${DeferredState[this.state].toLowerCase()}`);
this._reject(reason);
this._state = DeferredState.REJECTED;
}
/**
* Resolve the promise. Ignore if not pending.
*/
resolvePending(val) {
if (this._state === DeferredState.PENDING)
this.resolve(val);
}
/**
* Reject the promise. Ignore if not pending.
*/
rejectPending(reason) {
if (this._state === DeferredState.PENDING)
this.reject(reason);
}
}
exports.Deferred = Deferred;

View file

@ -0,0 +1,49 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DuplexStreamingCall = void 0;
/**
* A duplex streaming RPC call. This means that the clients sends an
* arbitrary amount of messages to the server, while at the same time,
* the server sends an arbitrary amount of messages to the client.
*/
class DuplexStreamingCall {
constructor(method, requestHeaders, request, headers, response, status, trailers) {
this.method = method;
this.requestHeaders = requestHeaders;
this.requests = request;
this.headers = headers;
this.responses = response;
this.status = status;
this.trailers = trailers;
}
/**
* Instead of awaiting the response status and trailers, you can
* just as well await this call itself to receive the server outcome.
* Note that it may still be valid to send more request messages.
*/
then(onfulfilled, onrejected) {
return this.promiseFinished().then(value => onfulfilled ? Promise.resolve(onfulfilled(value)) : value, reason => onrejected ? Promise.resolve(onrejected(reason)) : Promise.reject(reason));
}
promiseFinished() {
return __awaiter(this, void 0, void 0, function* () {
let [headers, status, trailers] = yield Promise.all([this.headers, this.status, this.trailers]);
return {
method: this.method,
requestHeaders: this.requestHeaders,
headers,
status,
trailers,
};
});
}
}
exports.DuplexStreamingCall = DuplexStreamingCall;

View file

@ -0,0 +1,38 @@
"use strict";
// Public API of the rpc runtime.
// Note: we do not use `export * from ...` to help tree shakers,
// webpack verbose output hints that this should be useful
Object.defineProperty(exports, "__esModule", { value: true });
var service_type_1 = require("./service-type");
Object.defineProperty(exports, "ServiceType", { enumerable: true, get: function () { return service_type_1.ServiceType; } });
var reflection_info_1 = require("./reflection-info");
Object.defineProperty(exports, "readMethodOptions", { enumerable: true, get: function () { return reflection_info_1.readMethodOptions; } });
Object.defineProperty(exports, "readMethodOption", { enumerable: true, get: function () { return reflection_info_1.readMethodOption; } });
Object.defineProperty(exports, "readServiceOption", { enumerable: true, get: function () { return reflection_info_1.readServiceOption; } });
var rpc_error_1 = require("./rpc-error");
Object.defineProperty(exports, "RpcError", { enumerable: true, get: function () { return rpc_error_1.RpcError; } });
var rpc_options_1 = require("./rpc-options");
Object.defineProperty(exports, "mergeRpcOptions", { enumerable: true, get: function () { return rpc_options_1.mergeRpcOptions; } });
var rpc_output_stream_1 = require("./rpc-output-stream");
Object.defineProperty(exports, "RpcOutputStreamController", { enumerable: true, get: function () { return rpc_output_stream_1.RpcOutputStreamController; } });
var test_transport_1 = require("./test-transport");
Object.defineProperty(exports, "TestTransport", { enumerable: true, get: function () { return test_transport_1.TestTransport; } });
var deferred_1 = require("./deferred");
Object.defineProperty(exports, "Deferred", { enumerable: true, get: function () { return deferred_1.Deferred; } });
Object.defineProperty(exports, "DeferredState", { enumerable: true, get: function () { return deferred_1.DeferredState; } });
var duplex_streaming_call_1 = require("./duplex-streaming-call");
Object.defineProperty(exports, "DuplexStreamingCall", { enumerable: true, get: function () { return duplex_streaming_call_1.DuplexStreamingCall; } });
var client_streaming_call_1 = require("./client-streaming-call");
Object.defineProperty(exports, "ClientStreamingCall", { enumerable: true, get: function () { return client_streaming_call_1.ClientStreamingCall; } });
var server_streaming_call_1 = require("./server-streaming-call");
Object.defineProperty(exports, "ServerStreamingCall", { enumerable: true, get: function () { return server_streaming_call_1.ServerStreamingCall; } });
var unary_call_1 = require("./unary-call");
Object.defineProperty(exports, "UnaryCall", { enumerable: true, get: function () { return unary_call_1.UnaryCall; } });
var rpc_interceptor_1 = require("./rpc-interceptor");
Object.defineProperty(exports, "stackIntercept", { enumerable: true, get: function () { return rpc_interceptor_1.stackIntercept; } });
Object.defineProperty(exports, "stackDuplexStreamingInterceptors", { enumerable: true, get: function () { return rpc_interceptor_1.stackDuplexStreamingInterceptors; } });
Object.defineProperty(exports, "stackClientStreamingInterceptors", { enumerable: true, get: function () { return rpc_interceptor_1.stackClientStreamingInterceptors; } });
Object.defineProperty(exports, "stackServerStreamingInterceptors", { enumerable: true, get: function () { return rpc_interceptor_1.stackServerStreamingInterceptors; } });
Object.defineProperty(exports, "stackUnaryInterceptors", { enumerable: true, get: function () { return rpc_interceptor_1.stackUnaryInterceptors; } });
var server_call_context_1 = require("./server-call-context");
Object.defineProperty(exports, "ServerCallContextController", { enumerable: true, get: function () { return server_call_context_1.ServerCallContextController; } });

View file

@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readServiceOption = exports.readMethodOption = exports.readMethodOptions = exports.normalizeMethodInfo = void 0;
const runtime_1 = require("@protobuf-ts/runtime");
/**
* Turns PartialMethodInfo into MethodInfo.
*/
function normalizeMethodInfo(method, service) {
var _a, _b, _c;
let m = method;
m.service = service;
m.localName = (_a = m.localName) !== null && _a !== void 0 ? _a : runtime_1.lowerCamelCase(m.name);
// noinspection PointlessBooleanExpressionJS
m.serverStreaming = !!m.serverStreaming;
// noinspection PointlessBooleanExpressionJS
m.clientStreaming = !!m.clientStreaming;
m.options = (_b = m.options) !== null && _b !== void 0 ? _b : {};
m.idempotency = (_c = m.idempotency) !== null && _c !== void 0 ? _c : undefined;
return m;
}
exports.normalizeMethodInfo = normalizeMethodInfo;
/**
* Read custom method options from a generated service client.
*
* @deprecated use readMethodOption()
*/
function readMethodOptions(service, methodName, extensionName, extensionType) {
var _a;
const options = (_a = service.methods.find((m, i) => m.localName === methodName || i === methodName)) === null || _a === void 0 ? void 0 : _a.options;
return options && options[extensionName] ? extensionType.fromJson(options[extensionName]) : undefined;
}
exports.readMethodOptions = readMethodOptions;
function readMethodOption(service, methodName, extensionName, extensionType) {
var _a;
const options = (_a = service.methods.find((m, i) => m.localName === methodName || i === methodName)) === null || _a === void 0 ? void 0 : _a.options;
if (!options) {
return undefined;
}
const optionVal = options[extensionName];
if (optionVal === undefined) {
return optionVal;
}
return extensionType ? extensionType.fromJson(optionVal) : optionVal;
}
exports.readMethodOption = readMethodOption;
function readServiceOption(service, extensionName, extensionType) {
const options = service.options;
if (!options) {
return undefined;
}
const optionVal = options[extensionName];
if (optionVal === undefined) {
return optionVal;
}
return extensionType ? extensionType.fromJson(optionVal) : optionVal;
}
exports.readServiceOption = readServiceOption;

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RpcError = void 0;
/**
* An error that occurred while calling a RPC method.
*/
class RpcError extends Error {
constructor(message, code = 'UNKNOWN', meta) {
super(message);
this.name = 'RpcError';
// see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#example
Object.setPrototypeOf(this, new.target.prototype);
this.code = code;
this.meta = meta !== null && meta !== void 0 ? meta : {};
}
toString() {
const l = [this.name + ': ' + this.message];
if (this.code) {
l.push('');
l.push('Code: ' + this.code);
}
if (this.serviceName && this.methodName) {
l.push('Method: ' + this.serviceName + '/' + this.methodName);
}
let m = Object.entries(this.meta);
if (m.length) {
l.push('');
l.push('Meta:');
for (let [k, v] of m) {
l.push(` ${k}: ${v}`);
}
}
return l.join('\n');
}
}
exports.RpcError = RpcError;

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.stackDuplexStreamingInterceptors = exports.stackClientStreamingInterceptors = exports.stackServerStreamingInterceptors = exports.stackUnaryInterceptors = exports.stackIntercept = void 0;
const runtime_1 = require("@protobuf-ts/runtime");
/**
* Creates a "stack" of of all interceptors specified in the given `RpcOptions`.
* Used by generated client implementations.
* @internal
*/
function stackIntercept(kind, transport, method, options, input) {
var _a, _b, _c, _d;
if (kind == "unary") {
let tail = (mtd, inp, opt) => transport.unary(mtd, inp, opt);
for (const curr of ((_a = options.interceptors) !== null && _a !== void 0 ? _a : []).filter(i => i.interceptUnary).reverse()) {
const next = tail;
tail = (mtd, inp, opt) => curr.interceptUnary(next, mtd, inp, opt);
}
return tail(method, input, options);
}
if (kind == "serverStreaming") {
let tail = (mtd, inp, opt) => transport.serverStreaming(mtd, inp, opt);
for (const curr of ((_b = options.interceptors) !== null && _b !== void 0 ? _b : []).filter(i => i.interceptServerStreaming).reverse()) {
const next = tail;
tail = (mtd, inp, opt) => curr.interceptServerStreaming(next, mtd, inp, opt);
}
return tail(method, input, options);
}
if (kind == "clientStreaming") {
let tail = (mtd, opt) => transport.clientStreaming(mtd, opt);
for (const curr of ((_c = options.interceptors) !== null && _c !== void 0 ? _c : []).filter(i => i.interceptClientStreaming).reverse()) {
const next = tail;
tail = (mtd, opt) => curr.interceptClientStreaming(next, mtd, opt);
}
return tail(method, options);
}
if (kind == "duplex") {
let tail = (mtd, opt) => transport.duplex(mtd, opt);
for (const curr of ((_d = options.interceptors) !== null && _d !== void 0 ? _d : []).filter(i => i.interceptDuplex).reverse()) {
const next = tail;
tail = (mtd, opt) => curr.interceptDuplex(next, mtd, opt);
}
return tail(method, options);
}
runtime_1.assertNever(kind);
}
exports.stackIntercept = stackIntercept;
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
function stackUnaryInterceptors(transport, method, input, options) {
return stackIntercept("unary", transport, method, options, input);
}
exports.stackUnaryInterceptors = stackUnaryInterceptors;
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
function stackServerStreamingInterceptors(transport, method, input, options) {
return stackIntercept("serverStreaming", transport, method, options, input);
}
exports.stackServerStreamingInterceptors = stackServerStreamingInterceptors;
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
function stackClientStreamingInterceptors(transport, method, options) {
return stackIntercept("clientStreaming", transport, method, options);
}
exports.stackClientStreamingInterceptors = stackClientStreamingInterceptors;
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
function stackDuplexStreamingInterceptors(transport, method, options) {
return stackIntercept("duplex", transport, method, options);
}
exports.stackDuplexStreamingInterceptors = stackDuplexStreamingInterceptors;

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeRpcOptions = void 0;
const runtime_1 = require("@protobuf-ts/runtime");
/**
* Merges custom RPC options with defaults. Returns a new instance and keeps
* the "defaults" and the "options" unmodified.
*
* Merges `RpcMetadata` "meta", overwriting values from "defaults" with
* values from "options". Does not append values to existing entries.
*
* Merges "jsonOptions", including "jsonOptions.typeRegistry", by creating
* a new array that contains types from "options.jsonOptions.typeRegistry"
* first, then types from "defaults.jsonOptions.typeRegistry".
*
* Merges "binaryOptions".
*
* Merges "interceptors" by creating a new array that contains interceptors
* from "defaults" first, then interceptors from "options".
*
* Works with objects that extend `RpcOptions`, but only if the added
* properties are of type Date, primitive like string, boolean, or Array
* of primitives. If you have other property types, you have to merge them
* yourself.
*/
function mergeRpcOptions(defaults, options) {
if (!options)
return defaults;
let o = {};
copy(defaults, o);
copy(options, o);
for (let key of Object.keys(options)) {
let val = options[key];
switch (key) {
case "jsonOptions":
o.jsonOptions = runtime_1.mergeJsonOptions(defaults.jsonOptions, o.jsonOptions);
break;
case "binaryOptions":
o.binaryOptions = runtime_1.mergeBinaryOptions(defaults.binaryOptions, o.binaryOptions);
break;
case "meta":
o.meta = {};
copy(defaults.meta, o.meta);
copy(options.meta, o.meta);
break;
case "interceptors":
o.interceptors = defaults.interceptors ? defaults.interceptors.concat(val) : val.concat();
break;
}
}
return o;
}
exports.mergeRpcOptions = mergeRpcOptions;
function copy(a, into) {
if (!a)
return;
let c = into;
for (let [k, v] of Object.entries(a)) {
if (v instanceof Date)
c[k] = new Date(v.getTime());
else if (Array.isArray(v))
c[k] = v.concat();
else
c[k] = v;
}
}

View file

@ -0,0 +1,172 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RpcOutputStreamController = void 0;
const deferred_1 = require("./deferred");
const runtime_1 = require("@protobuf-ts/runtime");
/**
* A `RpcOutputStream` that you control.
*/
class RpcOutputStreamController {
constructor() {
this._lis = {
nxt: [],
msg: [],
err: [],
cmp: [],
};
this._closed = false;
}
// --- RpcOutputStream callback API
onNext(callback) {
return this.addLis(callback, this._lis.nxt);
}
onMessage(callback) {
return this.addLis(callback, this._lis.msg);
}
onError(callback) {
return this.addLis(callback, this._lis.err);
}
onComplete(callback) {
return this.addLis(callback, this._lis.cmp);
}
addLis(callback, list) {
list.push(callback);
return () => {
let i = list.indexOf(callback);
if (i >= 0)
list.splice(i, 1);
};
}
// remove all listeners
clearLis() {
for (let l of Object.values(this._lis))
l.splice(0, l.length);
}
// --- Controller API
/**
* Is this stream already closed by a completion or error?
*/
get closed() {
return this._closed !== false;
}
/**
* Emit message, close with error, or close successfully, but only one
* at a time.
* Can be used to wrap a stream by using the other stream's `onNext`.
*/
notifyNext(message, error, complete) {
runtime_1.assert((message ? 1 : 0) + (error ? 1 : 0) + (complete ? 1 : 0) <= 1, 'only one emission at a time');
if (message)
this.notifyMessage(message);
if (error)
this.notifyError(error);
if (complete)
this.notifyComplete();
}
/**
* Emits a new message. Throws if stream is closed.
*
* Triggers onNext and onMessage callbacks.
*/
notifyMessage(message) {
runtime_1.assert(!this.closed, 'stream is closed');
this.pushIt({ value: message, done: false });
this._lis.msg.forEach(l => l(message));
this._lis.nxt.forEach(l => l(message, undefined, false));
}
/**
* Closes the stream with an error. Throws if stream is closed.
*
* Triggers onNext and onError callbacks.
*/
notifyError(error) {
runtime_1.assert(!this.closed, 'stream is closed');
this._closed = error;
this.pushIt(error);
this._lis.err.forEach(l => l(error));
this._lis.nxt.forEach(l => l(undefined, error, false));
this.clearLis();
}
/**
* Closes the stream successfully. Throws if stream is closed.
*
* Triggers onNext and onComplete callbacks.
*/
notifyComplete() {
runtime_1.assert(!this.closed, 'stream is closed');
this._closed = true;
this.pushIt({ value: null, done: true });
this._lis.cmp.forEach(l => l());
this._lis.nxt.forEach(l => l(undefined, undefined, true));
this.clearLis();
}
/**
* Creates an async iterator (that can be used with `for await {...}`)
* to consume the stream.
*
* Some things to note:
* - If an error occurs, the `for await` will throw it.
* - If an error occurred before the `for await` was started, `for await`
* will re-throw it.
* - If the stream is already complete, the `for await` will be empty.
* - If your `for await` consumes slower than the stream produces,
* for example because you are relaying messages in a slow operation,
* messages are queued.
*/
[Symbol.asyncIterator]() {
// init the iterator state, enabling pushIt()
if (!this._itState) {
this._itState = { q: [] };
}
// if we are closed, we are definitely not receiving any more messages.
// but we can't let the iterator get stuck. we want to either:
// a) finish the new iterator immediately, because we are completed
// b) reject the new iterator, because we errored
if (this._closed === true)
this.pushIt({ value: null, done: true });
else if (this._closed !== false)
this.pushIt(this._closed);
// the async iterator
return {
next: () => {
let state = this._itState;
runtime_1.assert(state, "bad state"); // if we don't have a state here, code is broken
// there should be no pending result.
// did the consumer call next() before we resolved our previous result promise?
runtime_1.assert(!state.p, "iterator contract broken");
// did we produce faster than the iterator consumed?
// return the oldest result from the queue.
let first = state.q.shift();
if (first)
return ("value" in first) ? Promise.resolve(first) : Promise.reject(first);
// we have no result ATM, but we promise one.
// as soon as we have a result, we must resolve promise.
state.p = new deferred_1.Deferred();
return state.p.promise;
},
};
}
// "push" a new iterator result.
// this either resolves a pending promise, or enqueues the result.
pushIt(result) {
let state = this._itState;
if (!state)
return;
// is the consumer waiting for us?
if (state.p) {
// yes, consumer is waiting for this promise.
const p = state.p;
runtime_1.assert(p.state == deferred_1.DeferredState.PENDING, "iterator contract broken");
// resolve the promise
("value" in result) ? p.resolve(result) : p.reject(result);
// must cleanup, otherwise iterator.next() would pick it up again.
delete state.p;
}
else {
// we are producing faster than the iterator consumes.
// push result onto queue.
state.q.push(result);
}
}
}
exports.RpcOutputStreamController = RpcOutputStreamController;

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View file

@ -0,0 +1,60 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServerCallContextController = void 0;
class ServerCallContextController {
constructor(method, headers, deadline, sendResponseHeadersFn, defaultStatus = { code: 'OK', detail: '' }) {
this._cancelled = false;
this._listeners = [];
this.method = method;
this.headers = headers;
this.deadline = deadline;
this.trailers = {};
this._sendRH = sendResponseHeadersFn;
this.status = defaultStatus;
}
/**
* Set the call cancelled.
*
* Invokes all callbacks registered with onCancel() and
* sets `cancelled = true`.
*/
notifyCancelled() {
if (!this._cancelled) {
this._cancelled = true;
for (let l of this._listeners) {
l();
}
}
}
/**
* Send response headers.
*/
sendResponseHeaders(data) {
this._sendRH(data);
}
/**
* Is the call cancelled?
*
* When the client closes the connection before the server
* is done, the call is cancelled.
*
* If you want to cancel a request on the server, throw a
* RpcError with the CANCELLED status code.
*/
get cancelled() {
return this._cancelled;
}
/**
* Add a callback for cancellation.
*/
onCancel(callback) {
const l = this._listeners;
l.push(callback);
return () => {
let i = l.indexOf(callback);
if (i >= 0)
l.splice(i, 1);
};
}
}
exports.ServerCallContextController = ServerCallContextController;

View file

@ -0,0 +1,50 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServerStreamingCall = void 0;
/**
* A server streaming RPC call. The client provides exactly one input message
* but the server may respond with 0, 1, or more messages.
*/
class ServerStreamingCall {
constructor(method, requestHeaders, request, headers, response, status, trailers) {
this.method = method;
this.requestHeaders = requestHeaders;
this.request = request;
this.headers = headers;
this.responses = response;
this.status = status;
this.trailers = trailers;
}
/**
* Instead of awaiting the response status and trailers, you can
* just as well await this call itself to receive the server outcome.
* You should first setup some listeners to the `request` to
* see the actual messages the server replied with.
*/
then(onfulfilled, onrejected) {
return this.promiseFinished().then(value => onfulfilled ? Promise.resolve(onfulfilled(value)) : value, reason => onrejected ? Promise.resolve(onrejected(reason)) : Promise.reject(reason));
}
promiseFinished() {
return __awaiter(this, void 0, void 0, function* () {
let [headers, status, trailers] = yield Promise.all([this.headers, this.status, this.trailers]);
return {
method: this.method,
requestHeaders: this.requestHeaders,
request: this.request,
headers,
status,
trailers,
};
});
}
}
exports.ServerStreamingCall = ServerStreamingCall;

View file

@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceType = void 0;
const reflection_info_1 = require("./reflection-info");
class ServiceType {
constructor(typeName, methods, options) {
this.typeName = typeName;
this.methods = methods.map(i => reflection_info_1.normalizeMethodInfo(i, this));
this.options = options !== null && options !== void 0 ? options : {};
}
}
exports.ServiceType = ServiceType;

View file

@ -0,0 +1,321 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TestTransport = void 0;
const rpc_error_1 = require("./rpc-error");
const runtime_1 = require("@protobuf-ts/runtime");
const rpc_output_stream_1 = require("./rpc-output-stream");
const rpc_options_1 = require("./rpc-options");
const unary_call_1 = require("./unary-call");
const server_streaming_call_1 = require("./server-streaming-call");
const client_streaming_call_1 = require("./client-streaming-call");
const duplex_streaming_call_1 = require("./duplex-streaming-call");
/**
* Transport for testing.
*/
class TestTransport {
/**
* Initialize with mock data. Omitted fields have default value.
*/
constructor(data) {
/**
* Suppress warning / error about uncaught rejections of
* "status" and "trailers".
*/
this.suppressUncaughtRejections = true;
this.headerDelay = 10;
this.responseDelay = 50;
this.betweenResponseDelay = 10;
this.afterResponseDelay = 10;
this.data = data !== null && data !== void 0 ? data : {};
}
/**
* Sent message(s) during the last operation.
*/
get sentMessages() {
if (this.lastInput instanceof TestInputStream) {
return this.lastInput.sent;
}
else if (typeof this.lastInput == "object") {
return [this.lastInput.single];
}
return [];
}
/**
* Sending message(s) completed?
*/
get sendComplete() {
if (this.lastInput instanceof TestInputStream) {
return this.lastInput.completed;
}
else if (typeof this.lastInput == "object") {
return true;
}
return false;
}
// Creates a promise for response headers from the mock data.
promiseHeaders() {
var _a;
const headers = (_a = this.data.headers) !== null && _a !== void 0 ? _a : TestTransport.defaultHeaders;
return headers instanceof rpc_error_1.RpcError
? Promise.reject(headers)
: Promise.resolve(headers);
}
// Creates a promise for a single, valid, message from the mock data.
promiseSingleResponse(method) {
if (this.data.response instanceof rpc_error_1.RpcError) {
return Promise.reject(this.data.response);
}
let r;
if (Array.isArray(this.data.response)) {
runtime_1.assert(this.data.response.length > 0);
r = this.data.response[0];
}
else if (this.data.response !== undefined) {
r = this.data.response;
}
else {
r = method.O.create();
}
runtime_1.assert(method.O.is(r));
return Promise.resolve(r);
}
/**
* Pushes response messages from the mock data to the output stream.
* If an error response, status or trailers are mocked, the stream is
* closed with the respective error.
* Otherwise, stream is completed successfully.
*
* The returned promise resolves when the stream is closed. It should
* not reject. If it does, code is broken.
*/
streamResponses(method, stream, abort) {
return __awaiter(this, void 0, void 0, function* () {
// normalize "data.response" into an array of valid output messages
const messages = [];
if (this.data.response === undefined) {
messages.push(method.O.create());
}
else if (Array.isArray(this.data.response)) {
for (let msg of this.data.response) {
runtime_1.assert(method.O.is(msg));
messages.push(msg);
}
}
else if (!(this.data.response instanceof rpc_error_1.RpcError)) {
runtime_1.assert(method.O.is(this.data.response));
messages.push(this.data.response);
}
// start the stream with an initial delay.
// if the request is cancelled, notify() error and exit.
try {
yield delay(this.responseDelay, abort)(undefined);
}
catch (error) {
stream.notifyError(error);
return;
}
// if error response was mocked, notify() error (stream is now closed with error) and exit.
if (this.data.response instanceof rpc_error_1.RpcError) {
stream.notifyError(this.data.response);
return;
}
// regular response messages were mocked. notify() them.
for (let msg of messages) {
stream.notifyMessage(msg);
// add a short delay between responses
// if the request is cancelled, notify() error and exit.
try {
yield delay(this.betweenResponseDelay, abort)(undefined);
}
catch (error) {
stream.notifyError(error);
return;
}
}
// error status was mocked, notify() error (stream is now closed with error) and exit.
if (this.data.status instanceof rpc_error_1.RpcError) {
stream.notifyError(this.data.status);
return;
}
// error trailers were mocked, notify() error (stream is now closed with error) and exit.
if (this.data.trailers instanceof rpc_error_1.RpcError) {
stream.notifyError(this.data.trailers);
return;
}
// stream completed successfully
stream.notifyComplete();
});
}
// Creates a promise for response status from the mock data.
promiseStatus() {
var _a;
const status = (_a = this.data.status) !== null && _a !== void 0 ? _a : TestTransport.defaultStatus;
return status instanceof rpc_error_1.RpcError
? Promise.reject(status)
: Promise.resolve(status);
}
// Creates a promise for response trailers from the mock data.
promiseTrailers() {
var _a;
const trailers = (_a = this.data.trailers) !== null && _a !== void 0 ? _a : TestTransport.defaultTrailers;
return trailers instanceof rpc_error_1.RpcError
? Promise.reject(trailers)
: Promise.resolve(trailers);
}
maybeSuppressUncaught(...promise) {
if (this.suppressUncaughtRejections) {
for (let p of promise) {
p.catch(() => {
});
}
}
}
mergeOptions(options) {
return rpc_options_1.mergeRpcOptions({}, options);
}
unary(method, input, options) {
var _a;
const requestHeaders = (_a = options.meta) !== null && _a !== void 0 ? _a : {}, headersPromise = this.promiseHeaders()
.then(delay(this.headerDelay, options.abort)), responsePromise = headersPromise
.catch(_ => {
})
.then(delay(this.responseDelay, options.abort))
.then(_ => this.promiseSingleResponse(method)), statusPromise = responsePromise
.catch(_ => {
})
.then(delay(this.afterResponseDelay, options.abort))
.then(_ => this.promiseStatus()), trailersPromise = responsePromise
.catch(_ => {
})
.then(delay(this.afterResponseDelay, options.abort))
.then(_ => this.promiseTrailers());
this.maybeSuppressUncaught(statusPromise, trailersPromise);
this.lastInput = { single: input };
return new unary_call_1.UnaryCall(method, requestHeaders, input, headersPromise, responsePromise, statusPromise, trailersPromise);
}
serverStreaming(method, input, options) {
var _a;
const requestHeaders = (_a = options.meta) !== null && _a !== void 0 ? _a : {}, headersPromise = this.promiseHeaders()
.then(delay(this.headerDelay, options.abort)), outputStream = new rpc_output_stream_1.RpcOutputStreamController(), responseStreamClosedPromise = headersPromise
.then(delay(this.responseDelay, options.abort))
.catch(() => {
})
.then(() => this.streamResponses(method, outputStream, options.abort))
.then(delay(this.afterResponseDelay, options.abort)), statusPromise = responseStreamClosedPromise
.then(() => this.promiseStatus()), trailersPromise = responseStreamClosedPromise
.then(() => this.promiseTrailers());
this.maybeSuppressUncaught(statusPromise, trailersPromise);
this.lastInput = { single: input };
return new server_streaming_call_1.ServerStreamingCall(method, requestHeaders, input, headersPromise, outputStream, statusPromise, trailersPromise);
}
clientStreaming(method, options) {
var _a;
const requestHeaders = (_a = options.meta) !== null && _a !== void 0 ? _a : {}, headersPromise = this.promiseHeaders()
.then(delay(this.headerDelay, options.abort)), responsePromise = headersPromise
.catch(_ => {
})
.then(delay(this.responseDelay, options.abort))
.then(_ => this.promiseSingleResponse(method)), statusPromise = responsePromise
.catch(_ => {
})
.then(delay(this.afterResponseDelay, options.abort))
.then(_ => this.promiseStatus()), trailersPromise = responsePromise
.catch(_ => {
})
.then(delay(this.afterResponseDelay, options.abort))
.then(_ => this.promiseTrailers());
this.maybeSuppressUncaught(statusPromise, trailersPromise);
this.lastInput = new TestInputStream(this.data, options.abort);
return new client_streaming_call_1.ClientStreamingCall(method, requestHeaders, this.lastInput, headersPromise, responsePromise, statusPromise, trailersPromise);
}
duplex(method, options) {
var _a;
const requestHeaders = (_a = options.meta) !== null && _a !== void 0 ? _a : {}, headersPromise = this.promiseHeaders()
.then(delay(this.headerDelay, options.abort)), outputStream = new rpc_output_stream_1.RpcOutputStreamController(), responseStreamClosedPromise = headersPromise
.then(delay(this.responseDelay, options.abort))
.catch(() => {
})
.then(() => this.streamResponses(method, outputStream, options.abort))
.then(delay(this.afterResponseDelay, options.abort)), statusPromise = responseStreamClosedPromise
.then(() => this.promiseStatus()), trailersPromise = responseStreamClosedPromise
.then(() => this.promiseTrailers());
this.maybeSuppressUncaught(statusPromise, trailersPromise);
this.lastInput = new TestInputStream(this.data, options.abort);
return new duplex_streaming_call_1.DuplexStreamingCall(method, requestHeaders, this.lastInput, headersPromise, outputStream, statusPromise, trailersPromise);
}
}
exports.TestTransport = TestTransport;
TestTransport.defaultHeaders = {
responseHeader: "test"
};
TestTransport.defaultStatus = {
code: "OK", detail: "all good"
};
TestTransport.defaultTrailers = {
responseTrailer: "test"
};
function delay(ms, abort) {
return (v) => new Promise((resolve, reject) => {
if (abort === null || abort === void 0 ? void 0 : abort.aborted) {
reject(new rpc_error_1.RpcError("user cancel", "CANCELLED"));
}
else {
const id = setTimeout(() => resolve(v), ms);
if (abort) {
abort.addEventListener("abort", ev => {
clearTimeout(id);
reject(new rpc_error_1.RpcError("user cancel", "CANCELLED"));
});
}
}
});
}
class TestInputStream {
constructor(data, abort) {
this._completed = false;
this._sent = [];
this.data = data;
this.abort = abort;
}
get sent() {
return this._sent;
}
get completed() {
return this._completed;
}
send(message) {
if (this.data.inputMessage instanceof rpc_error_1.RpcError) {
return Promise.reject(this.data.inputMessage);
}
const delayMs = this.data.inputMessage === undefined
? 10
: this.data.inputMessage;
return Promise.resolve(undefined)
.then(() => {
this._sent.push(message);
})
.then(delay(delayMs, this.abort));
}
complete() {
if (this.data.inputComplete instanceof rpc_error_1.RpcError) {
return Promise.reject(this.data.inputComplete);
}
const delayMs = this.data.inputComplete === undefined
? 10
: this.data.inputComplete;
return Promise.resolve(undefined)
.then(() => {
this._completed = true;
})
.then(delay(delayMs, this.abort));
}
}

View file

@ -0,0 +1,49 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnaryCall = void 0;
/**
* A unary RPC call. Unary means there is exactly one input message and
* exactly one output message unless an error occurred.
*/
class UnaryCall {
constructor(method, requestHeaders, request, headers, response, status, trailers) {
this.method = method;
this.requestHeaders = requestHeaders;
this.request = request;
this.headers = headers;
this.response = response;
this.status = status;
this.trailers = trailers;
}
/**
* If you are only interested in the final outcome of this call,
* you can await it to receive a `FinishedUnaryCall`.
*/
then(onfulfilled, onrejected) {
return this.promiseFinished().then(value => onfulfilled ? Promise.resolve(onfulfilled(value)) : value, reason => onrejected ? Promise.resolve(onrejected(reason)) : Promise.reject(reason));
}
promiseFinished() {
return __awaiter(this, void 0, void 0, function* () {
let [headers, response, status, trailers] = yield Promise.all([this.headers, this.response, this.status, this.trailers]);
return {
method: this.method,
requestHeaders: this.requestHeaders,
request: this.request,
headers,
response,
status,
trailers
};
});
}
}
exports.UnaryCall = UnaryCall;

View file

@ -0,0 +1,46 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/**
* A client streaming RPC call. This means that the clients sends 0, 1, or
* more messages to the server, and the server replies with exactly one
* message.
*/
export class ClientStreamingCall {
constructor(method, requestHeaders, request, headers, response, status, trailers) {
this.method = method;
this.requestHeaders = requestHeaders;
this.requests = request;
this.headers = headers;
this.response = response;
this.status = status;
this.trailers = trailers;
}
/**
* Instead of awaiting the response status and trailers, you can
* just as well await this call itself to receive the server outcome.
* Note that it may still be valid to send more request messages.
*/
then(onfulfilled, onrejected) {
return this.promiseFinished().then(value => onfulfilled ? Promise.resolve(onfulfilled(value)) : value, reason => onrejected ? Promise.resolve(onrejected(reason)) : Promise.reject(reason));
}
promiseFinished() {
return __awaiter(this, void 0, void 0, function* () {
let [headers, response, status, trailers] = yield Promise.all([this.headers, this.response, this.status, this.trailers]);
return {
method: this.method,
requestHeaders: this.requestHeaders,
headers,
response,
status,
trailers
};
});
}
}

View file

@ -0,0 +1,82 @@
export var DeferredState;
(function (DeferredState) {
DeferredState[DeferredState["PENDING"] = 0] = "PENDING";
DeferredState[DeferredState["REJECTED"] = 1] = "REJECTED";
DeferredState[DeferredState["RESOLVED"] = 2] = "RESOLVED";
})(DeferredState || (DeferredState = {}));
/**
* A deferred promise. This is a "controller" for a promise, which lets you
* pass a promise around and reject or resolve it from the outside.
*
* Warning: This class is to be used with care. Using it can make code very
* difficult to read. It is intended for use in library code that exposes
* promises, not for regular business logic.
*/
export class Deferred {
/**
* @param preventUnhandledRejectionWarning - prevents the warning
* "Unhandled Promise rejection" by adding a noop rejection handler.
* Working with calls returned from the runtime-rpc package in an
* async function usually means awaiting one call property after
* the other. This means that the "status" is not being awaited when
* an earlier await for the "headers" is rejected. This causes the
* "unhandled promise reject" warning. A more correct behaviour for
* calls might be to become aware whether at least one of the
* promises is handled and swallow the rejection warning for the
* others.
*/
constructor(preventUnhandledRejectionWarning = true) {
this._state = DeferredState.PENDING;
this._promise = new Promise((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
});
if (preventUnhandledRejectionWarning) {
this._promise.catch(_ => { });
}
}
/**
* Get the current state of the promise.
*/
get state() {
return this._state;
}
/**
* Get the deferred promise.
*/
get promise() {
return this._promise;
}
/**
* Resolve the promise. Throws if the promise is already resolved or rejected.
*/
resolve(value) {
if (this.state !== DeferredState.PENDING)
throw new Error(`cannot resolve ${DeferredState[this.state].toLowerCase()}`);
this._resolve(value);
this._state = DeferredState.RESOLVED;
}
/**
* Reject the promise. Throws if the promise is already resolved or rejected.
*/
reject(reason) {
if (this.state !== DeferredState.PENDING)
throw new Error(`cannot reject ${DeferredState[this.state].toLowerCase()}`);
this._reject(reason);
this._state = DeferredState.REJECTED;
}
/**
* Resolve the promise. Ignore if not pending.
*/
resolvePending(val) {
if (this._state === DeferredState.PENDING)
this.resolve(val);
}
/**
* Reject the promise. Ignore if not pending.
*/
rejectPending(reason) {
if (this._state === DeferredState.PENDING)
this.reject(reason);
}
}

View file

@ -0,0 +1,45 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/**
* A duplex streaming RPC call. This means that the clients sends an
* arbitrary amount of messages to the server, while at the same time,
* the server sends an arbitrary amount of messages to the client.
*/
export class DuplexStreamingCall {
constructor(method, requestHeaders, request, headers, response, status, trailers) {
this.method = method;
this.requestHeaders = requestHeaders;
this.requests = request;
this.headers = headers;
this.responses = response;
this.status = status;
this.trailers = trailers;
}
/**
* Instead of awaiting the response status and trailers, you can
* just as well await this call itself to receive the server outcome.
* Note that it may still be valid to send more request messages.
*/
then(onfulfilled, onrejected) {
return this.promiseFinished().then(value => onfulfilled ? Promise.resolve(onfulfilled(value)) : value, reason => onrejected ? Promise.resolve(onrejected(reason)) : Promise.reject(reason));
}
promiseFinished() {
return __awaiter(this, void 0, void 0, function* () {
let [headers, status, trailers] = yield Promise.all([this.headers, this.status, this.trailers]);
return {
method: this.method,
requestHeaders: this.requestHeaders,
headers,
status,
trailers,
};
});
}
}

View file

@ -0,0 +1,16 @@
// Public API of the rpc runtime.
// Note: we do not use `export * from ...` to help tree shakers,
// webpack verbose output hints that this should be useful
export { ServiceType } from './service-type';
export { readMethodOptions, readMethodOption, readServiceOption } from './reflection-info';
export { RpcError } from './rpc-error';
export { mergeRpcOptions } from './rpc-options';
export { RpcOutputStreamController } from './rpc-output-stream';
export { TestTransport } from './test-transport';
export { Deferred, DeferredState } from './deferred';
export { DuplexStreamingCall } from './duplex-streaming-call';
export { ClientStreamingCall } from './client-streaming-call';
export { ServerStreamingCall } from './server-streaming-call';
export { UnaryCall } from './unary-call';
export { stackIntercept, stackDuplexStreamingInterceptors, stackClientStreamingInterceptors, stackServerStreamingInterceptors, stackUnaryInterceptors } from './rpc-interceptor';
export { ServerCallContextController } from './server-call-context';

View file

@ -0,0 +1,50 @@
import { lowerCamelCase } from "@protobuf-ts/runtime";
/**
* Turns PartialMethodInfo into MethodInfo.
*/
export function normalizeMethodInfo(method, service) {
var _a, _b, _c;
let m = method;
m.service = service;
m.localName = (_a = m.localName) !== null && _a !== void 0 ? _a : lowerCamelCase(m.name);
// noinspection PointlessBooleanExpressionJS
m.serverStreaming = !!m.serverStreaming;
// noinspection PointlessBooleanExpressionJS
m.clientStreaming = !!m.clientStreaming;
m.options = (_b = m.options) !== null && _b !== void 0 ? _b : {};
m.idempotency = (_c = m.idempotency) !== null && _c !== void 0 ? _c : undefined;
return m;
}
/**
* Read custom method options from a generated service client.
*
* @deprecated use readMethodOption()
*/
export function readMethodOptions(service, methodName, extensionName, extensionType) {
var _a;
const options = (_a = service.methods.find((m, i) => m.localName === methodName || i === methodName)) === null || _a === void 0 ? void 0 : _a.options;
return options && options[extensionName] ? extensionType.fromJson(options[extensionName]) : undefined;
}
export function readMethodOption(service, methodName, extensionName, extensionType) {
var _a;
const options = (_a = service.methods.find((m, i) => m.localName === methodName || i === methodName)) === null || _a === void 0 ? void 0 : _a.options;
if (!options) {
return undefined;
}
const optionVal = options[extensionName];
if (optionVal === undefined) {
return optionVal;
}
return extensionType ? extensionType.fromJson(optionVal) : optionVal;
}
export function readServiceOption(service, extensionName, extensionType) {
const options = service.options;
if (!options) {
return undefined;
}
const optionVal = options[extensionName];
if (optionVal === undefined) {
return optionVal;
}
return extensionType ? extensionType.fromJson(optionVal) : optionVal;
}

View file

@ -0,0 +1,32 @@
/**
* An error that occurred while calling a RPC method.
*/
export class RpcError extends Error {
constructor(message, code = 'UNKNOWN', meta) {
super(message);
this.name = 'RpcError';
// see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#example
Object.setPrototypeOf(this, new.target.prototype);
this.code = code;
this.meta = meta !== null && meta !== void 0 ? meta : {};
}
toString() {
const l = [this.name + ': ' + this.message];
if (this.code) {
l.push('');
l.push('Code: ' + this.code);
}
if (this.serviceName && this.methodName) {
l.push('Method: ' + this.serviceName + '/' + this.methodName);
}
let m = Object.entries(this.meta);
if (m.length) {
l.push('');
l.push('Meta:');
for (let [k, v] of m) {
l.push(` ${k}: ${v}`);
}
}
return l.join('\n');
}
}

View file

@ -0,0 +1,66 @@
import { assertNever } from "@protobuf-ts/runtime";
/**
* Creates a "stack" of of all interceptors specified in the given `RpcOptions`.
* Used by generated client implementations.
* @internal
*/
export function stackIntercept(kind, transport, method, options, input) {
var _a, _b, _c, _d;
if (kind == "unary") {
let tail = (mtd, inp, opt) => transport.unary(mtd, inp, opt);
for (const curr of ((_a = options.interceptors) !== null && _a !== void 0 ? _a : []).filter(i => i.interceptUnary).reverse()) {
const next = tail;
tail = (mtd, inp, opt) => curr.interceptUnary(next, mtd, inp, opt);
}
return tail(method, input, options);
}
if (kind == "serverStreaming") {
let tail = (mtd, inp, opt) => transport.serverStreaming(mtd, inp, opt);
for (const curr of ((_b = options.interceptors) !== null && _b !== void 0 ? _b : []).filter(i => i.interceptServerStreaming).reverse()) {
const next = tail;
tail = (mtd, inp, opt) => curr.interceptServerStreaming(next, mtd, inp, opt);
}
return tail(method, input, options);
}
if (kind == "clientStreaming") {
let tail = (mtd, opt) => transport.clientStreaming(mtd, opt);
for (const curr of ((_c = options.interceptors) !== null && _c !== void 0 ? _c : []).filter(i => i.interceptClientStreaming).reverse()) {
const next = tail;
tail = (mtd, opt) => curr.interceptClientStreaming(next, mtd, opt);
}
return tail(method, options);
}
if (kind == "duplex") {
let tail = (mtd, opt) => transport.duplex(mtd, opt);
for (const curr of ((_d = options.interceptors) !== null && _d !== void 0 ? _d : []).filter(i => i.interceptDuplex).reverse()) {
const next = tail;
tail = (mtd, opt) => curr.interceptDuplex(next, mtd, opt);
}
return tail(method, options);
}
assertNever(kind);
}
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
export function stackUnaryInterceptors(transport, method, input, options) {
return stackIntercept("unary", transport, method, options, input);
}
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
export function stackServerStreamingInterceptors(transport, method, input, options) {
return stackIntercept("serverStreaming", transport, method, options, input);
}
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
export function stackClientStreamingInterceptors(transport, method, options) {
return stackIntercept("clientStreaming", transport, method, options);
}
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
export function stackDuplexStreamingInterceptors(transport, method, options) {
return stackIntercept("duplex", transport, method, options);
}

View file

@ -0,0 +1,62 @@
import { mergeBinaryOptions, mergeJsonOptions } from "@protobuf-ts/runtime";
/**
* Merges custom RPC options with defaults. Returns a new instance and keeps
* the "defaults" and the "options" unmodified.
*
* Merges `RpcMetadata` "meta", overwriting values from "defaults" with
* values from "options". Does not append values to existing entries.
*
* Merges "jsonOptions", including "jsonOptions.typeRegistry", by creating
* a new array that contains types from "options.jsonOptions.typeRegistry"
* first, then types from "defaults.jsonOptions.typeRegistry".
*
* Merges "binaryOptions".
*
* Merges "interceptors" by creating a new array that contains interceptors
* from "defaults" first, then interceptors from "options".
*
* Works with objects that extend `RpcOptions`, but only if the added
* properties are of type Date, primitive like string, boolean, or Array
* of primitives. If you have other property types, you have to merge them
* yourself.
*/
export function mergeRpcOptions(defaults, options) {
if (!options)
return defaults;
let o = {};
copy(defaults, o);
copy(options, o);
for (let key of Object.keys(options)) {
let val = options[key];
switch (key) {
case "jsonOptions":
o.jsonOptions = mergeJsonOptions(defaults.jsonOptions, o.jsonOptions);
break;
case "binaryOptions":
o.binaryOptions = mergeBinaryOptions(defaults.binaryOptions, o.binaryOptions);
break;
case "meta":
o.meta = {};
copy(defaults.meta, o.meta);
copy(options.meta, o.meta);
break;
case "interceptors":
o.interceptors = defaults.interceptors ? defaults.interceptors.concat(val) : val.concat();
break;
}
}
return o;
}
function copy(a, into) {
if (!a)
return;
let c = into;
for (let [k, v] of Object.entries(a)) {
if (v instanceof Date)
c[k] = new Date(v.getTime());
else if (Array.isArray(v))
c[k] = v.concat();
else
c[k] = v;
}
}

View file

@ -0,0 +1,168 @@
import { Deferred, DeferredState } from "./deferred";
import { assert } from "@protobuf-ts/runtime";
/**
* A `RpcOutputStream` that you control.
*/
export class RpcOutputStreamController {
constructor() {
this._lis = {
nxt: [],
msg: [],
err: [],
cmp: [],
};
this._closed = false;
}
// --- RpcOutputStream callback API
onNext(callback) {
return this.addLis(callback, this._lis.nxt);
}
onMessage(callback) {
return this.addLis(callback, this._lis.msg);
}
onError(callback) {
return this.addLis(callback, this._lis.err);
}
onComplete(callback) {
return this.addLis(callback, this._lis.cmp);
}
addLis(callback, list) {
list.push(callback);
return () => {
let i = list.indexOf(callback);
if (i >= 0)
list.splice(i, 1);
};
}
// remove all listeners
clearLis() {
for (let l of Object.values(this._lis))
l.splice(0, l.length);
}
// --- Controller API
/**
* Is this stream already closed by a completion or error?
*/
get closed() {
return this._closed !== false;
}
/**
* Emit message, close with error, or close successfully, but only one
* at a time.
* Can be used to wrap a stream by using the other stream's `onNext`.
*/
notifyNext(message, error, complete) {
assert((message ? 1 : 0) + (error ? 1 : 0) + (complete ? 1 : 0) <= 1, 'only one emission at a time');
if (message)
this.notifyMessage(message);
if (error)
this.notifyError(error);
if (complete)
this.notifyComplete();
}
/**
* Emits a new message. Throws if stream is closed.
*
* Triggers onNext and onMessage callbacks.
*/
notifyMessage(message) {
assert(!this.closed, 'stream is closed');
this.pushIt({ value: message, done: false });
this._lis.msg.forEach(l => l(message));
this._lis.nxt.forEach(l => l(message, undefined, false));
}
/**
* Closes the stream with an error. Throws if stream is closed.
*
* Triggers onNext and onError callbacks.
*/
notifyError(error) {
assert(!this.closed, 'stream is closed');
this._closed = error;
this.pushIt(error);
this._lis.err.forEach(l => l(error));
this._lis.nxt.forEach(l => l(undefined, error, false));
this.clearLis();
}
/**
* Closes the stream successfully. Throws if stream is closed.
*
* Triggers onNext and onComplete callbacks.
*/
notifyComplete() {
assert(!this.closed, 'stream is closed');
this._closed = true;
this.pushIt({ value: null, done: true });
this._lis.cmp.forEach(l => l());
this._lis.nxt.forEach(l => l(undefined, undefined, true));
this.clearLis();
}
/**
* Creates an async iterator (that can be used with `for await {...}`)
* to consume the stream.
*
* Some things to note:
* - If an error occurs, the `for await` will throw it.
* - If an error occurred before the `for await` was started, `for await`
* will re-throw it.
* - If the stream is already complete, the `for await` will be empty.
* - If your `for await` consumes slower than the stream produces,
* for example because you are relaying messages in a slow operation,
* messages are queued.
*/
[Symbol.asyncIterator]() {
// init the iterator state, enabling pushIt()
if (!this._itState) {
this._itState = { q: [] };
}
// if we are closed, we are definitely not receiving any more messages.
// but we can't let the iterator get stuck. we want to either:
// a) finish the new iterator immediately, because we are completed
// b) reject the new iterator, because we errored
if (this._closed === true)
this.pushIt({ value: null, done: true });
else if (this._closed !== false)
this.pushIt(this._closed);
// the async iterator
return {
next: () => {
let state = this._itState;
assert(state, "bad state"); // if we don't have a state here, code is broken
// there should be no pending result.
// did the consumer call next() before we resolved our previous result promise?
assert(!state.p, "iterator contract broken");
// did we produce faster than the iterator consumed?
// return the oldest result from the queue.
let first = state.q.shift();
if (first)
return ("value" in first) ? Promise.resolve(first) : Promise.reject(first);
// we have no result ATM, but we promise one.
// as soon as we have a result, we must resolve promise.
state.p = new Deferred();
return state.p.promise;
},
};
}
// "push" a new iterator result.
// this either resolves a pending promise, or enqueues the result.
pushIt(result) {
let state = this._itState;
if (!state)
return;
// is the consumer waiting for us?
if (state.p) {
// yes, consumer is waiting for this promise.
const p = state.p;
assert(p.state == DeferredState.PENDING, "iterator contract broken");
// resolve the promise
("value" in result) ? p.resolve(result) : p.reject(result);
// must cleanup, otherwise iterator.next() would pick it up again.
delete state.p;
}
else {
// we are producing faster than the iterator consumes.
// push result onto queue.
state.q.push(result);
}
}
}

View file

@ -0,0 +1,56 @@
export class ServerCallContextController {
constructor(method, headers, deadline, sendResponseHeadersFn, defaultStatus = { code: 'OK', detail: '' }) {
this._cancelled = false;
this._listeners = [];
this.method = method;
this.headers = headers;
this.deadline = deadline;
this.trailers = {};
this._sendRH = sendResponseHeadersFn;
this.status = defaultStatus;
}
/**
* Set the call cancelled.
*
* Invokes all callbacks registered with onCancel() and
* sets `cancelled = true`.
*/
notifyCancelled() {
if (!this._cancelled) {
this._cancelled = true;
for (let l of this._listeners) {
l();
}
}
}
/**
* Send response headers.
*/
sendResponseHeaders(data) {
this._sendRH(data);
}
/**
* Is the call cancelled?
*
* When the client closes the connection before the server
* is done, the call is cancelled.
*
* If you want to cancel a request on the server, throw a
* RpcError with the CANCELLED status code.
*/
get cancelled() {
return this._cancelled;
}
/**
* Add a callback for cancellation.
*/
onCancel(callback) {
const l = this._listeners;
l.push(callback);
return () => {
let i = l.indexOf(callback);
if (i >= 0)
l.splice(i, 1);
};
}
}

View file

@ -0,0 +1,46 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/**
* A server streaming RPC call. The client provides exactly one input message
* but the server may respond with 0, 1, or more messages.
*/
export class ServerStreamingCall {
constructor(method, requestHeaders, request, headers, response, status, trailers) {
this.method = method;
this.requestHeaders = requestHeaders;
this.request = request;
this.headers = headers;
this.responses = response;
this.status = status;
this.trailers = trailers;
}
/**
* Instead of awaiting the response status and trailers, you can
* just as well await this call itself to receive the server outcome.
* You should first setup some listeners to the `request` to
* see the actual messages the server replied with.
*/
then(onfulfilled, onrejected) {
return this.promiseFinished().then(value => onfulfilled ? Promise.resolve(onfulfilled(value)) : value, reason => onrejected ? Promise.resolve(onrejected(reason)) : Promise.reject(reason));
}
promiseFinished() {
return __awaiter(this, void 0, void 0, function* () {
let [headers, status, trailers] = yield Promise.all([this.headers, this.status, this.trailers]);
return {
method: this.method,
requestHeaders: this.requestHeaders,
request: this.request,
headers,
status,
trailers,
};
});
}
}

View file

@ -0,0 +1,8 @@
import { normalizeMethodInfo } from "./reflection-info";
export class ServiceType {
constructor(typeName, methods, options) {
this.typeName = typeName;
this.methods = methods.map(i => normalizeMethodInfo(i, this));
this.options = options !== null && options !== void 0 ? options : {};
}
}

View file

@ -0,0 +1,317 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { RpcError } from "./rpc-error";
import { assert } from "@protobuf-ts/runtime";
import { RpcOutputStreamController } from "./rpc-output-stream";
import { mergeRpcOptions } from "./rpc-options";
import { UnaryCall } from "./unary-call";
import { ServerStreamingCall } from "./server-streaming-call";
import { ClientStreamingCall } from "./client-streaming-call";
import { DuplexStreamingCall } from "./duplex-streaming-call";
/**
* Transport for testing.
*/
export class TestTransport {
/**
* Initialize with mock data. Omitted fields have default value.
*/
constructor(data) {
/**
* Suppress warning / error about uncaught rejections of
* "status" and "trailers".
*/
this.suppressUncaughtRejections = true;
this.headerDelay = 10;
this.responseDelay = 50;
this.betweenResponseDelay = 10;
this.afterResponseDelay = 10;
this.data = data !== null && data !== void 0 ? data : {};
}
/**
* Sent message(s) during the last operation.
*/
get sentMessages() {
if (this.lastInput instanceof TestInputStream) {
return this.lastInput.sent;
}
else if (typeof this.lastInput == "object") {
return [this.lastInput.single];
}
return [];
}
/**
* Sending message(s) completed?
*/
get sendComplete() {
if (this.lastInput instanceof TestInputStream) {
return this.lastInput.completed;
}
else if (typeof this.lastInput == "object") {
return true;
}
return false;
}
// Creates a promise for response headers from the mock data.
promiseHeaders() {
var _a;
const headers = (_a = this.data.headers) !== null && _a !== void 0 ? _a : TestTransport.defaultHeaders;
return headers instanceof RpcError
? Promise.reject(headers)
: Promise.resolve(headers);
}
// Creates a promise for a single, valid, message from the mock data.
promiseSingleResponse(method) {
if (this.data.response instanceof RpcError) {
return Promise.reject(this.data.response);
}
let r;
if (Array.isArray(this.data.response)) {
assert(this.data.response.length > 0);
r = this.data.response[0];
}
else if (this.data.response !== undefined) {
r = this.data.response;
}
else {
r = method.O.create();
}
assert(method.O.is(r));
return Promise.resolve(r);
}
/**
* Pushes response messages from the mock data to the output stream.
* If an error response, status or trailers are mocked, the stream is
* closed with the respective error.
* Otherwise, stream is completed successfully.
*
* The returned promise resolves when the stream is closed. It should
* not reject. If it does, code is broken.
*/
streamResponses(method, stream, abort) {
return __awaiter(this, void 0, void 0, function* () {
// normalize "data.response" into an array of valid output messages
const messages = [];
if (this.data.response === undefined) {
messages.push(method.O.create());
}
else if (Array.isArray(this.data.response)) {
for (let msg of this.data.response) {
assert(method.O.is(msg));
messages.push(msg);
}
}
else if (!(this.data.response instanceof RpcError)) {
assert(method.O.is(this.data.response));
messages.push(this.data.response);
}
// start the stream with an initial delay.
// if the request is cancelled, notify() error and exit.
try {
yield delay(this.responseDelay, abort)(undefined);
}
catch (error) {
stream.notifyError(error);
return;
}
// if error response was mocked, notify() error (stream is now closed with error) and exit.
if (this.data.response instanceof RpcError) {
stream.notifyError(this.data.response);
return;
}
// regular response messages were mocked. notify() them.
for (let msg of messages) {
stream.notifyMessage(msg);
// add a short delay between responses
// if the request is cancelled, notify() error and exit.
try {
yield delay(this.betweenResponseDelay, abort)(undefined);
}
catch (error) {
stream.notifyError(error);
return;
}
}
// error status was mocked, notify() error (stream is now closed with error) and exit.
if (this.data.status instanceof RpcError) {
stream.notifyError(this.data.status);
return;
}
// error trailers were mocked, notify() error (stream is now closed with error) and exit.
if (this.data.trailers instanceof RpcError) {
stream.notifyError(this.data.trailers);
return;
}
// stream completed successfully
stream.notifyComplete();
});
}
// Creates a promise for response status from the mock data.
promiseStatus() {
var _a;
const status = (_a = this.data.status) !== null && _a !== void 0 ? _a : TestTransport.defaultStatus;
return status instanceof RpcError
? Promise.reject(status)
: Promise.resolve(status);
}
// Creates a promise for response trailers from the mock data.
promiseTrailers() {
var _a;
const trailers = (_a = this.data.trailers) !== null && _a !== void 0 ? _a : TestTransport.defaultTrailers;
return trailers instanceof RpcError
? Promise.reject(trailers)
: Promise.resolve(trailers);
}
maybeSuppressUncaught(...promise) {
if (this.suppressUncaughtRejections) {
for (let p of promise) {
p.catch(() => {
});
}
}
}
mergeOptions(options) {
return mergeRpcOptions({}, options);
}
unary(method, input, options) {
var _a;
const requestHeaders = (_a = options.meta) !== null && _a !== void 0 ? _a : {}, headersPromise = this.promiseHeaders()
.then(delay(this.headerDelay, options.abort)), responsePromise = headersPromise
.catch(_ => {
})
.then(delay(this.responseDelay, options.abort))
.then(_ => this.promiseSingleResponse(method)), statusPromise = responsePromise
.catch(_ => {
})
.then(delay(this.afterResponseDelay, options.abort))
.then(_ => this.promiseStatus()), trailersPromise = responsePromise
.catch(_ => {
})
.then(delay(this.afterResponseDelay, options.abort))
.then(_ => this.promiseTrailers());
this.maybeSuppressUncaught(statusPromise, trailersPromise);
this.lastInput = { single: input };
return new UnaryCall(method, requestHeaders, input, headersPromise, responsePromise, statusPromise, trailersPromise);
}
serverStreaming(method, input, options) {
var _a;
const requestHeaders = (_a = options.meta) !== null && _a !== void 0 ? _a : {}, headersPromise = this.promiseHeaders()
.then(delay(this.headerDelay, options.abort)), outputStream = new RpcOutputStreamController(), responseStreamClosedPromise = headersPromise
.then(delay(this.responseDelay, options.abort))
.catch(() => {
})
.then(() => this.streamResponses(method, outputStream, options.abort))
.then(delay(this.afterResponseDelay, options.abort)), statusPromise = responseStreamClosedPromise
.then(() => this.promiseStatus()), trailersPromise = responseStreamClosedPromise
.then(() => this.promiseTrailers());
this.maybeSuppressUncaught(statusPromise, trailersPromise);
this.lastInput = { single: input };
return new ServerStreamingCall(method, requestHeaders, input, headersPromise, outputStream, statusPromise, trailersPromise);
}
clientStreaming(method, options) {
var _a;
const requestHeaders = (_a = options.meta) !== null && _a !== void 0 ? _a : {}, headersPromise = this.promiseHeaders()
.then(delay(this.headerDelay, options.abort)), responsePromise = headersPromise
.catch(_ => {
})
.then(delay(this.responseDelay, options.abort))
.then(_ => this.promiseSingleResponse(method)), statusPromise = responsePromise
.catch(_ => {
})
.then(delay(this.afterResponseDelay, options.abort))
.then(_ => this.promiseStatus()), trailersPromise = responsePromise
.catch(_ => {
})
.then(delay(this.afterResponseDelay, options.abort))
.then(_ => this.promiseTrailers());
this.maybeSuppressUncaught(statusPromise, trailersPromise);
this.lastInput = new TestInputStream(this.data, options.abort);
return new ClientStreamingCall(method, requestHeaders, this.lastInput, headersPromise, responsePromise, statusPromise, trailersPromise);
}
duplex(method, options) {
var _a;
const requestHeaders = (_a = options.meta) !== null && _a !== void 0 ? _a : {}, headersPromise = this.promiseHeaders()
.then(delay(this.headerDelay, options.abort)), outputStream = new RpcOutputStreamController(), responseStreamClosedPromise = headersPromise
.then(delay(this.responseDelay, options.abort))
.catch(() => {
})
.then(() => this.streamResponses(method, outputStream, options.abort))
.then(delay(this.afterResponseDelay, options.abort)), statusPromise = responseStreamClosedPromise
.then(() => this.promiseStatus()), trailersPromise = responseStreamClosedPromise
.then(() => this.promiseTrailers());
this.maybeSuppressUncaught(statusPromise, trailersPromise);
this.lastInput = new TestInputStream(this.data, options.abort);
return new DuplexStreamingCall(method, requestHeaders, this.lastInput, headersPromise, outputStream, statusPromise, trailersPromise);
}
}
TestTransport.defaultHeaders = {
responseHeader: "test"
};
TestTransport.defaultStatus = {
code: "OK", detail: "all good"
};
TestTransport.defaultTrailers = {
responseTrailer: "test"
};
function delay(ms, abort) {
return (v) => new Promise((resolve, reject) => {
if (abort === null || abort === void 0 ? void 0 : abort.aborted) {
reject(new RpcError("user cancel", "CANCELLED"));
}
else {
const id = setTimeout(() => resolve(v), ms);
if (abort) {
abort.addEventListener("abort", ev => {
clearTimeout(id);
reject(new RpcError("user cancel", "CANCELLED"));
});
}
}
});
}
class TestInputStream {
constructor(data, abort) {
this._completed = false;
this._sent = [];
this.data = data;
this.abort = abort;
}
get sent() {
return this._sent;
}
get completed() {
return this._completed;
}
send(message) {
if (this.data.inputMessage instanceof RpcError) {
return Promise.reject(this.data.inputMessage);
}
const delayMs = this.data.inputMessage === undefined
? 10
: this.data.inputMessage;
return Promise.resolve(undefined)
.then(() => {
this._sent.push(message);
})
.then(delay(delayMs, this.abort));
}
complete() {
if (this.data.inputComplete instanceof RpcError) {
return Promise.reject(this.data.inputComplete);
}
const delayMs = this.data.inputComplete === undefined
? 10
: this.data.inputComplete;
return Promise.resolve(undefined)
.then(() => {
this._completed = true;
})
.then(delay(delayMs, this.abort));
}
}

View file

@ -0,0 +1,45 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/**
* A unary RPC call. Unary means there is exactly one input message and
* exactly one output message unless an error occurred.
*/
export class UnaryCall {
constructor(method, requestHeaders, request, headers, response, status, trailers) {
this.method = method;
this.requestHeaders = requestHeaders;
this.request = request;
this.headers = headers;
this.response = response;
this.status = status;
this.trailers = trailers;
}
/**
* If you are only interested in the final outcome of this call,
* you can await it to receive a `FinishedUnaryCall`.
*/
then(onfulfilled, onrejected) {
return this.promiseFinished().then(value => onfulfilled ? Promise.resolve(onfulfilled(value)) : value, reason => onrejected ? Promise.resolve(onrejected(reason)) : Promise.reject(reason));
}
promiseFinished() {
return __awaiter(this, void 0, void 0, function* () {
let [headers, response, status, trailers] = yield Promise.all([this.headers, this.response, this.status, this.trailers]);
return {
method: this.method,
requestHeaders: this.requestHeaders,
request: this.request,
headers,
response,
status,
trailers
};
});
}
}

View file

@ -0,0 +1,97 @@
import type { RpcCallShared } from "./rpc-call-shared";
import type { RpcInputStream } from "./rpc-input-stream";
import type { RpcStatus } from "./rpc-status";
import type { RpcMetadata } from "./rpc-metadata";
import type { MethodInfo } from "./reflection-info";
/**
* A client streaming RPC call. This means that the clients sends 0, 1, or
* more messages to the server, and the server replies with exactly one
* message.
*/
export declare class ClientStreamingCall<I extends object = object, O extends object = object> implements RpcCallShared<I, O> {
/**
* Reflection information about this call.
*/
readonly method: MethodInfo<I, O>;
/**
* Request headers being sent with the request.
*
* Request headers are provided in the `meta` property of the
* `RpcOptions` passed to a call.
*/
readonly requestHeaders: Readonly<RpcMetadata>;
/**
* Request messages from the client.
*/
readonly requests: RpcInputStream<I>;
/**
* The response headers that the server sent.
*
* This promise will reject with a `RpcError` when the server sends a
* error status code.
*/
readonly headers: Promise<RpcMetadata>;
/**
* The message the server replied with.
*/
readonly response: Promise<O>;
/**
* The response status the server replied with.
*
* This promise will resolve when the server has finished the request
* successfully.
*
* If the server replies with an error status, this promise will
* reject with a `RpcError`.
*/
readonly status: Promise<RpcStatus>;
/**
* The trailers the server attached to the response.
*
* This promise will resolve when the server has finished the request
* successfully.
*
* If the server replies with an error status, this promise will
* reject with a `RpcError`.
*/
readonly trailers: Promise<RpcMetadata>;
constructor(method: MethodInfo<I, O>, requestHeaders: Readonly<RpcMetadata>, request: RpcInputStream<I>, headers: Promise<RpcMetadata>, response: Promise<O>, status: Promise<RpcStatus>, trailers: Promise<RpcMetadata>);
/**
* Instead of awaiting the response status and trailers, you can
* just as well await this call itself to receive the server outcome.
* Note that it may still be valid to send more request messages.
*/
then<TResult1 = FinishedClientStreamingCall<I, O>, TResult2 = never>(onfulfilled?: ((value: FinishedClientStreamingCall<I, O>) => (PromiseLike<TResult1> | TResult1)) | undefined | null, onrejected?: ((reason: any) => (PromiseLike<TResult2> | TResult2)) | undefined | null): Promise<TResult1 | TResult2>;
private promiseFinished;
}
/**
* A completed client streaming RPC call. The server will not send any more
* messages, but it may still be valid to send request messages.
*/
export interface FinishedClientStreamingCall<I extends object, O extends object> {
/**
* Reflection information about this call.
*/
readonly method: MethodInfo<I, O>;
/**
* Request headers being sent with the request.
*/
readonly requestHeaders: Readonly<RpcMetadata>;
/**
* The response headers that the server sent.
*/
readonly headers: RpcMetadata;
/**
* The message the server replied with.
*/
readonly response: O;
/**
* The response status the server replied with.
* The status code will always be OK.
*/
readonly status: RpcStatus;
/**
* The trailers the server attached to the response.
*/
readonly trailers: RpcMetadata;
}

View file

@ -0,0 +1,56 @@
export declare enum DeferredState {
PENDING = 0,
REJECTED = 1,
RESOLVED = 2
}
/**
* A deferred promise. This is a "controller" for a promise, which lets you
* pass a promise around and reject or resolve it from the outside.
*
* Warning: This class is to be used with care. Using it can make code very
* difficult to read. It is intended for use in library code that exposes
* promises, not for regular business logic.
*/
export declare class Deferred<T> {
/**
* Get the current state of the promise.
*/
get state(): DeferredState;
/**
* Get the deferred promise.
*/
get promise(): Promise<T>;
private readonly _promise;
private _state;
private _resolve;
private _reject;
/**
* @param preventUnhandledRejectionWarning - prevents the warning
* "Unhandled Promise rejection" by adding a noop rejection handler.
* Working with calls returned from the runtime-rpc package in an
* async function usually means awaiting one call property after
* the other. This means that the "status" is not being awaited when
* an earlier await for the "headers" is rejected. This causes the
* "unhandled promise reject" warning. A more correct behaviour for
* calls might be to become aware whether at least one of the
* promises is handled and swallow the rejection warning for the
* others.
*/
constructor(preventUnhandledRejectionWarning?: boolean);
/**
* Resolve the promise. Throws if the promise is already resolved or rejected.
*/
resolve(value: T | PromiseLike<T>): void;
/**
* Reject the promise. Throws if the promise is already resolved or rejected.
*/
reject(reason: any): void;
/**
* Resolve the promise. Ignore if not pending.
*/
resolvePending(val: T | PromiseLike<T>): void;
/**
* Reject the promise. Ignore if not pending.
*/
rejectPending(reason: any): void;
}

View file

@ -0,0 +1,94 @@
import type { RpcCallShared } from "./rpc-call-shared";
import type { RpcInputStream } from "./rpc-input-stream";
import type { RpcOutputStream } from "./rpc-output-stream";
import type { RpcStatus } from "./rpc-status";
import type { MethodInfo } from "./reflection-info";
import type { RpcMetadata } from "./rpc-metadata";
/**
* A duplex streaming RPC call. This means that the clients sends an
* arbitrary amount of messages to the server, while at the same time,
* the server sends an arbitrary amount of messages to the client.
*/
export declare class DuplexStreamingCall<I extends object = object, O extends object = object> implements RpcCallShared<I, O> {
/**
* Reflection information about this call.
*/
readonly method: MethodInfo<I, O>;
/**
* Request headers being sent with the request.
*
* Request headers are provided in the `meta` property of the
* `RpcOptions` passed to a call.
*/
readonly requestHeaders: Readonly<RpcMetadata>;
/**
* Request messages from the client.
*/
readonly requests: RpcInputStream<I>;
/**
* The response headers that the server sent.
*
* This promise will reject with a `RpcError` when the server sends a
* error status code.
*/
readonly headers: Promise<RpcMetadata>;
/**
* Response messages from the server.
*/
readonly responses: RpcOutputStream<O>;
/**
* The response status the server replied with.
*
* This promise will resolve when the server has finished the request
* successfully.
*
* If the server replies with an error status, this promise will
* reject with a `RpcError`.
*/
readonly status: Promise<RpcStatus>;
/**
* The trailers the server attached to the response.
*
* This promise will resolve when the server has finished the request
* successfully.
*
* If the server replies with an error status, this promise will
* reject with a `RpcError`.
*/
readonly trailers: Promise<RpcMetadata>;
constructor(method: MethodInfo<I, O>, requestHeaders: Readonly<RpcMetadata>, request: RpcInputStream<I>, headers: Promise<RpcMetadata>, response: RpcOutputStream<O>, status: Promise<RpcStatus>, trailers: Promise<RpcMetadata>);
/**
* Instead of awaiting the response status and trailers, you can
* just as well await this call itself to receive the server outcome.
* Note that it may still be valid to send more request messages.
*/
then<TResult1 = FinishedDuplexStreamingCall<I, O>, TResult2 = never>(onfulfilled?: ((value: FinishedDuplexStreamingCall<I, O>) => (PromiseLike<TResult1> | TResult1)) | undefined | null, onrejected?: ((reason: any) => (PromiseLike<TResult2> | TResult2)) | undefined | null): Promise<TResult1 | TResult2>;
private promiseFinished;
}
/**
* A completed duplex streaming RPC call. The server will not send any more
* messages, but it may still be valid to send request messages.
*/
export interface FinishedDuplexStreamingCall<I extends object, O extends object> {
/**
* Reflection information about this call.
*/
readonly method: MethodInfo<I, O>;
/**
* Request headers being sent with the request.
*/
readonly requestHeaders: Readonly<RpcMetadata>;
/**
* The response headers that the server sent.
*/
readonly headers: RpcMetadata;
/**
* The response status the server replied with.
* The status code will always be OK.
*/
readonly status: RpcStatus;
/**
* The trailers the server attached to the response.
*/
readonly trailers: RpcMetadata;
}

View file

@ -0,0 +1,17 @@
export { ServiceType } from './service-type';
export { MethodInfo, PartialMethodInfo, ServiceInfo, readMethodOptions, readMethodOption, readServiceOption } from './reflection-info';
export { RpcError } from './rpc-error';
export { RpcMetadata } from './rpc-metadata';
export { RpcOptions, mergeRpcOptions } from './rpc-options';
export { RpcInputStream } from './rpc-input-stream';
export { RpcOutputStream, RpcOutputStreamController } from './rpc-output-stream';
export { RpcStatus } from './rpc-status';
export { RpcTransport } from './rpc-transport';
export { TestTransport } from './test-transport';
export { Deferred, DeferredState } from './deferred';
export { DuplexStreamingCall } from './duplex-streaming-call';
export { ClientStreamingCall } from './client-streaming-call';
export { ServerStreamingCall, FinishedServerStreamingCall } from './server-streaming-call';
export { UnaryCall, FinishedUnaryCall } from './unary-call';
export { NextUnaryFn, RpcInterceptor, NextClientStreamingFn, NextDuplexStreamingFn, NextServerStreamingFn, stackIntercept, stackDuplexStreamingInterceptors, stackClientStreamingInterceptors, stackServerStreamingInterceptors, stackUnaryInterceptors } from './rpc-interceptor';
export { ServerCallContext, ServerCallContextController } from './server-call-context';

View file

@ -0,0 +1,143 @@
import type { IMessageType, JsonValue } from "@protobuf-ts/runtime";
/**
* Describes a protobuf service for runtime reflection.
*/
export interface ServiceInfo {
/**
* The protobuf type name of the service, including package name if
* present.
*/
readonly typeName: string;
/**
* Information for each rpc method of the service, in the order of
* declaration in the source .proto.
*/
readonly methods: MethodInfo[];
/**
* Contains custom service options from the .proto source in JSON format.
*/
readonly options: {
[extensionName: string]: JsonValue;
};
}
/**
* Describes a protobuf service method for runtime reflection.
*/
export interface MethodInfo<I extends object = any, O extends object = any> {
/**
* The service this method belongs to.
*/
readonly service: ServiceInfo;
/**
* The name of the method as declared in .proto
*/
readonly name: string;
/**
* The name of the method in the runtime.
*/
readonly localName: string;
/**
* The idempotency level as specified in .proto.
*
* For example, the following method declaration will set
* `idempotency` to 'NO_SIDE_EFFECTS'.
*
* ```proto
* rpc Foo (FooRequest) returns (FooResponse) {
* option idempotency_level = NO_SIDE_EFFECTS
* }
* ```
*
* See `google/protobuf/descriptor.proto`, `MethodOptions`.
*/
readonly idempotency: undefined | 'NO_SIDE_EFFECTS' | 'IDEMPOTENT';
/**
* Was the rpc declared with server streaming?
*
* Example declaration:
*
* ```proto
* rpc Foo (FooRequest) returns (stream FooResponse);
* ```
*/
readonly serverStreaming: boolean;
/**
* Was the rpc declared with client streaming?
*
* Example declaration:
*
* ```proto
* rpc Foo (stream FooRequest) returns (FooResponse);
* ```
*/
readonly clientStreaming: boolean;
/**
* The generated type handler for the input message.
* Provides methods to encode / decode binary or JSON format.
*/
readonly I: IMessageType<I>;
/**
* The generated type handler for the output message.
* Provides methods to encode / decode binary or JSON format.
*/
readonly O: IMessageType<O>;
/**
* Contains custom method options from the .proto source in JSON format.
*/
readonly options: {
[extensionName: string]: JsonValue;
};
}
/**
* Version of `MethodInfo` that does not include "service", and also allows
* the following properties to be omitted:
* - "localName": can be omitted if equal to lowerCamelCase(name)
* - "serverStreaming": omitting means `false`
* - "clientStreaming": omitting means `false`
* - "options"
*/
export declare type PartialMethodInfo<I extends object = any, O extends object = any> = PartialPartial<Omit<MethodInfo<I, O>, "service">, "localName" | "idempotency" | "serverStreaming" | "clientStreaming" | "options">;
declare type PartialPartial<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>;
/**
* Turns PartialMethodInfo into MethodInfo.
*/
export declare function normalizeMethodInfo<I extends object = any, O extends object = any>(method: PartialMethodInfo<I, O>, service: ServiceInfo): MethodInfo<I, O>;
/**
* Read custom method options from a generated service client.
*
* @deprecated use readMethodOption()
*/
export declare function readMethodOptions<T extends object>(service: ServiceInfo, methodName: string | number, extensionName: string, extensionType: IMessageType<T>): T | undefined;
/**
* Read a custom method option.
*
* ```proto
* service MyService {
* rpc Get (Req) returns (Res) {
* option (acme.rpc_opt) = true;
* };
* }
* ```
*
* ```typescript
* let val = readMethodOption(MyService, 'get', 'acme.rpc_opt')
* ```
*/
export declare function readMethodOption<T extends object>(service: ServiceInfo, methodName: string | number, extensionName: string): JsonValue | undefined;
export declare function readMethodOption<T extends object>(service: ServiceInfo, methodName: string | number, extensionName: string, extensionType: IMessageType<T>): T | undefined;
/**
* Read a custom service option.
*
* ```proto
* service MyService {
* option (acme.service_opt) = true;
* }
* ```
*
* ```typescript
* let val = readServiceOption(MyService, 'acme.service_opt')
* ```
*/
export declare function readServiceOption<T extends object>(service: ServiceInfo, extensionName: string): JsonValue | undefined;
export declare function readServiceOption<T extends object>(service: ServiceInfo, extensionName: string, extensionType: IMessageType<T>): T | undefined;
export {};

View file

@ -0,0 +1,43 @@
import type { MethodInfo } from "./reflection-info";
import type { RpcStatus } from "./rpc-status";
import type { RpcMetadata } from "./rpc-metadata";
export interface RpcCallShared<I extends object, O extends object> {
/**
* Reflection information about this call.
*/
readonly method: MethodInfo<I, O>;
/**
* Request headers being sent with the request.
*
* Request headers are provided in the `meta` property of the
* `RpcOptions` passed to a call.
*/
readonly requestHeaders: Readonly<RpcMetadata>;
/**
* The response headers that the server sent.
*
* This promise will reject with a `RpcError` when the server sends an
* error status code.
*/
readonly headers: Promise<RpcMetadata>;
/**
* The response status the server replied with.
*
* This promise will resolve when the server has finished the request
* successfully.
*
* If the server replies with an error status, this promise will
* reject with a `RpcError`.
*/
readonly status: Promise<RpcStatus>;
/**
* The trailers the server attached to the response.
*
* This promise will resolve when the server has finished the request
* successfully.
*
* If the server replies with an error status, this promise will
* reject with a `RpcError`.
*/
readonly trailers: Promise<RpcMetadata>;
}

View file

@ -0,0 +1,40 @@
import type { RpcMetadata } from "./rpc-metadata";
/**
* An error that occurred while calling a RPC method.
*/
export declare class RpcError extends Error {
/**
* A status code as string. The value depends on the `RpcTransport` being
* used.
*
* For gRPC, it will be the string value of a StatusCode enum value
* https://github.com/grpc/grpc/blob/a19d8dcfb50caa81cddc25bc1a6afdd7a2f497b7/include/grpcpp/impl/codegen/status_code_enum.h#L24
*
* For Twirp, it will be one of the Twirp error codes as string:
* https://twitchtv.github.io/twirp/docs/spec_v5.html#error-codes
*/
code: string;
/**
* Metadata related to the failed call.
*/
meta: RpcMetadata;
/**
* The name of the RPC method that was called as declared in .proto
*/
methodName?: string;
/**
* The name of the RPC service that was called as declared in .proto
*
* It will be in the form of:
* - package name
* - dot "."
* - service name
*
* If the service was declared without a package, the package name and dot
* are omitted.
*/
serviceName?: string;
name: string;
constructor(message: string, code?: string, meta?: RpcMetadata);
toString(): string;
}

View file

@ -0,0 +1,16 @@
/**
* A stream of input messages.
*/
export interface RpcInputStream<T> {
/**
* Send a message down the stream.
* Only one message can be send at a time.
*/
send(message: T): Promise<void>;
/**
* Complete / close the stream.
* Can only be called if there is no pending send().
* No send() should follow this call.
*/
complete(): Promise<void>;
}

View file

@ -0,0 +1,121 @@
import type { ServerStreamingCall } from "./server-streaming-call";
import type { ClientStreamingCall } from "./client-streaming-call";
import type { DuplexStreamingCall } from "./duplex-streaming-call";
import type { RpcTransport } from "./rpc-transport";
import type { MethodInfo } from "./reflection-info";
import type { RpcOptions } from "./rpc-options";
import type { UnaryCall } from "./unary-call";
/**
* Interceptors can be used to manipulate request and response data.
*
* They are commonly used to add authentication metadata, log requests
* or implement client side caching.
*
* Interceptors are stacked. Call next() to invoke the next interceptor
* on the stack. To manipulate the request, change the data passed to
* next(). To manipulate a response, change the data returned by next().
*
* The following example adds an 'Authorization' header to unary calls:
*
* ```typescript
* interceptUnary(next, method, input, options): UnaryCall {
* if (!options.meta) {
* options.meta = {};
* }
* options.meta['Authorization'] = 'xxx';
* return next(method, input, options);
* }
* ```
*
* The following example intercepts server streaming calls. Every
* message that the server sends is emitted twice to the client:
*
* ```typescript
* interceptServerStreaming(next, method, input, options) {
* let original = next(method, input, options);
* let response = new RpcOutputStreamController();
* original.response.onNext((message, error, done) => {
* if (message) {
* response.notifyMessage(message);
* response.notifyMessage(message);
* }
* if (error)
* response.notifyError(error);
* if (done)
* response.notifyComplete();
* });
* return new ServerStreamingCall(
* original.method,
* original.requestHeaders,
* original.request,
* original.headers,
* response,
* original.status,
* original.trailers
* );
* }
* ```
*
*/
export interface RpcInterceptor {
interceptUnary?(next: NextUnaryFn, method: MethodInfo, input: object, options: RpcOptions): UnaryCall;
interceptServerStreaming?(next: NextServerStreamingFn, method: MethodInfo, input: object, options: RpcOptions): ServerStreamingCall;
interceptClientStreaming?(next: NextClientStreamingFn, method: MethodInfo, options: RpcOptions): ClientStreamingCall;
interceptDuplex?(next: NextDuplexStreamingFn, method: MethodInfo, options: RpcOptions): DuplexStreamingCall;
}
/**
* Invokes the next interceptor on the stack and returns its result.
*/
export declare type NextUnaryFn = (method: MethodInfo, input: object, options: RpcOptions) => UnaryCall;
/**
* Invokes the next interceptor on the stack and returns its result.
*/
export declare type NextServerStreamingFn = (method: MethodInfo, input: object, options: RpcOptions) => ServerStreamingCall;
/**
* Invokes the next interceptor on the stack and returns its result.
*/
export declare type NextClientStreamingFn = (method: MethodInfo, options: RpcOptions) => ClientStreamingCall;
/**
* Invokes the next interceptor on the stack and returns its result.
*/
export declare type NextDuplexStreamingFn = (method: MethodInfo, options: RpcOptions) => DuplexStreamingCall;
/**
* Creates a "stack" of of all unary interceptors specified in the given `RpcOptions`.
* Used by generated client implementations.
* @internal
*/
export declare function stackIntercept<I extends object, O extends object>(kind: "unary", transport: RpcTransport, method: MethodInfo<I, O>, options: RpcOptions, input: I): UnaryCall<I, O>;
/**
* Creates a "stack" of of all server streaming interceptors specified in the given `RpcOptions`.
* Used by generated client implementations.
* @internal
*/
export declare function stackIntercept<I extends object, O extends object>(kind: "serverStreaming", transport: RpcTransport, method: MethodInfo<I, O>, options: RpcOptions, input: I): ServerStreamingCall<I, O>;
/**
* Creates a "stack" of of all client streaming interceptors specified in the given `RpcOptions`.
* Used by generated client implementations.
* @internal
*/
export declare function stackIntercept<I extends object, O extends object>(kind: "clientStreaming", transport: RpcTransport, method: MethodInfo<I, O>, options: RpcOptions): ClientStreamingCall<I, O>;
/**
* Creates a "stack" of of all duplex streaming interceptors specified in the given `RpcOptions`.
* Used by generated client implementations.
* @internal
*/
export declare function stackIntercept<I extends object, O extends object>(kind: "duplex", transport: RpcTransport, method: MethodInfo<I, O>, options: RpcOptions): DuplexStreamingCall<I, O>;
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
export declare function stackUnaryInterceptors<I extends object, O extends object>(transport: RpcTransport, method: MethodInfo<I, O>, input: I, options: RpcOptions): UnaryCall<I, O>;
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
export declare function stackServerStreamingInterceptors<I extends object, O extends object>(transport: RpcTransport, method: MethodInfo<I, O>, input: I, options: RpcOptions): ServerStreamingCall<I, O>;
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
export declare function stackClientStreamingInterceptors<I extends object, O extends object>(transport: RpcTransport, method: MethodInfo<I, O>, options: RpcOptions): ClientStreamingCall<I, O>;
/**
* @deprecated replaced by `stackIntercept()`, still here to support older generated code
*/
export declare function stackDuplexStreamingInterceptors<I extends object, O extends object>(transport: RpcTransport, method: MethodInfo<I, O>, options: RpcOptions): DuplexStreamingCall<I, O>;

View file

@ -0,0 +1,20 @@
/**
* RPC metadata provide optional additional information about a request or
* response.
*
* They can be transmitted at:
* - the start of a request (a.k.a. request headers)
* - the start of a response (a.k.a. response headers)
* - the end of a response (a.k.a. response trailers)
*
* Keys should only contain the characters a-z 0-9 _ . -
*
* Values can be US ASCII or binary. If a key ends with `-bin`, it contains
* binary data in base64 encoding.
*
* You can encode protobuf messages as binary metadata values, including
* `google.protobuf.Any`.
*/
export interface RpcMetadata {
[key: string]: string | string[];
}

View file

@ -0,0 +1,75 @@
import type { RpcMetadata } from "./rpc-metadata";
import type { BinaryReadOptions, BinaryWriteOptions, JsonReadOptions, JsonWriteOptions } from "@protobuf-ts/runtime";
import type { RpcInterceptor } from "./rpc-interceptor";
/**
* User-provided options for Remote Procedure Calls.
*
* Every generated service method accepts these options.
* They are passed on to the `RpcTransport` and can be evaluated there.
*/
export interface RpcOptions {
/**
* Meta data for the call.
*
* RPC meta data are simple key-value pairs that usually translate
* directly to HTTP request headers.
*
* If a key ends with `-bin`, it should contain binary data in base64
* encoding, allowing you to send serialized messages.
*/
meta?: RpcMetadata;
/**
* Timeout for the call in milliseconds.
* If a Date object is given, it is used as a deadline.
*/
timeout?: number | Date;
/**
* Interceptors can be used to manipulate request and response data.
* The most common use case is adding a "Authorization" header.
*/
interceptors?: RpcInterceptor[];
/**
* Options for the JSON wire format.
*
* To send or receive `google.protobuf.Any` in JSON format, you must
* provide `jsonOptions.typeRegistry` so that the runtime can discriminate
* the packed type.
*/
jsonOptions?: Partial<JsonReadOptions & JsonWriteOptions>;
/**
* Options for the binary wire format.
*/
binaryOptions?: Partial<BinaryReadOptions & BinaryWriteOptions>;
/**
* A signal to cancel a call. Can be created with an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).
* The npm package `abort-controller` provides a polyfill for Node.js.
*/
abort?: AbortSignal;
/**
* A `RpcTransport` implementation may allow arbitrary
* other options.
*/
[extra: string]: unknown;
}
/**
* Merges custom RPC options with defaults. Returns a new instance and keeps
* the "defaults" and the "options" unmodified.
*
* Merges `RpcMetadata` "meta", overwriting values from "defaults" with
* values from "options". Does not append values to existing entries.
*
* Merges "jsonOptions", including "jsonOptions.typeRegistry", by creating
* a new array that contains types from "options.jsonOptions.typeRegistry"
* first, then types from "defaults.jsonOptions.typeRegistry".
*
* Merges "binaryOptions".
*
* Merges "interceptors" by creating a new array that contains interceptors
* from "defaults" first, then interceptors from "options".
*
* Works with objects that extend `RpcOptions`, but only if the added
* properties are of type Date, primitive like string, boolean, or Array
* of primitives. If you have other property types, you have to merge them
* yourself.
*/
export declare function mergeRpcOptions<T extends RpcOptions>(defaults: T, options?: Partial<T>): T;

View file

@ -0,0 +1,106 @@
/**
* A stream of response messages. Messages can be read from the stream via
* the AsyncIterable interface:
*
* ```typescript
* for await (let message of response) {...
* ```
*
* Some things to note:
* - If an error occurs, the `for await` will throw it.
* - If an error occurred before the `for await` was started, `for await`
* will re-throw it.
* - If the stream is already complete, the `for await` will be empty.
* - If your `for await` consumes slower than the stream produces,
* for example because you are relaying messages in a slow operation,
* messages are queued.
*/
export interface RpcOutputStream<T extends object = object> extends AsyncIterable<T> {
/**
* Add a callback for every new datum.
* If a new message arrived, the "message" argument is set.
* If an error occurred, the "error" argument is set.
* If the stream is complete, the "complete" argument is `true`.
* Only one of the arguments is used at a time.
*/
onNext(callback: NextCallback<T>): RemoveListenerFn;
/**
* Add a callback for every new message.
*/
onMessage(callback: MessageCallback<T>): RemoveListenerFn;
/**
* Add a callback for stream completion.
* Called only when all messages have been read without error.
* The stream is closed when this callback is called.
*/
onComplete(callback: CompleteCallback): RemoveListenerFn;
/**
* Add a callback for errors.
* The stream is closed when this callback is called.
*/
onError(callback: ErrorCallback): RemoveListenerFn;
}
declare type NextCallback<T extends object> = (message: T | undefined, error: Error | undefined, complete: boolean) => void;
declare type MessageCallback<T extends object> = (message: T) => void;
declare type CompleteCallback = () => void;
declare type ErrorCallback = (reason: Error) => void;
declare type RemoveListenerFn = () => void;
/**
* A `RpcOutputStream` that you control.
*/
export declare class RpcOutputStreamController<T extends object = object> {
constructor();
onNext(callback: NextCallback<T>): RemoveListenerFn;
onMessage(callback: MessageCallback<T>): RemoveListenerFn;
onError(callback: ErrorCallback): RemoveListenerFn;
onComplete(callback: CompleteCallback): RemoveListenerFn;
private addLis;
private clearLis;
private readonly _lis;
/**
* Is this stream already closed by a completion or error?
*/
get closed(): boolean;
/**
* Emit message, close with error, or close successfully, but only one
* at a time.
* Can be used to wrap a stream by using the other stream's `onNext`.
*/
notifyNext(message: T | undefined, error: Error | undefined, complete: boolean): void;
/**
* Emits a new message. Throws if stream is closed.
*
* Triggers onNext and onMessage callbacks.
*/
notifyMessage(message: T): void;
/**
* Closes the stream with an error. Throws if stream is closed.
*
* Triggers onNext and onError callbacks.
*/
notifyError(error: Error): void;
/**
* Closes the stream successfully. Throws if stream is closed.
*
* Triggers onNext and onComplete callbacks.
*/
notifyComplete(): void;
private _closed;
private _itState;
/**
* Creates an async iterator (that can be used with `for await {...}`)
* to consume the stream.
*
* Some things to note:
* - If an error occurs, the `for await` will throw it.
* - If an error occurred before the `for await` was started, `for await`
* will re-throw it.
* - If the stream is already complete, the `for await` will be empty.
* - If your `for await` consumes slower than the stream produces,
* for example because you are relaying messages in a slow operation,
* messages are queued.
*/
[Symbol.asyncIterator](): AsyncIterator<T>;
private pushIt;
}
export {};

View file

@ -0,0 +1,24 @@
/**
* A RPC status consists of a code and a text message.
*
* The status is usually returned from the server as a response trailer,
* but a `RpcTransport` may also read the status from response headers.
*/
export interface RpcStatus {
/**
* A status code as a string. The value depends on the `RpcTransport` being
* used.
*
* For gRPC, it will be the string value of a StatusCode enum value
* https://github.com/grpc/grpc/blob/a19d8dcfb50caa81cddc25bc1a6afdd7a2f497b7/include/grpcpp/impl/codegen/status_code_enum.h#L24
*
* For Twirp, it will be one of the Twirp error codes as string:
* https://twitchtv.github.io/twirp/docs/spec_v5.html#error-codes
*
*/
code: string;
/**
* A text message that may describe the condition.
*/
detail: string;
}

View file

@ -0,0 +1,57 @@
import type { UnaryCall } from "./unary-call";
import type { ServerStreamingCall } from "./server-streaming-call";
import type { ClientStreamingCall } from "./client-streaming-call";
import type { DuplexStreamingCall } from "./duplex-streaming-call";
import type { MethodInfo } from "./reflection-info";
import type { RpcOptions } from "./rpc-options";
/**
* A `RpcTransport` executes Remote Procedure Calls defined by a protobuf
* service.
*
* This interface is the contract between a generated service client and
* some wire protocol like grpc, grpc-web, Twirp or other.
*
* The transport receives reflection information about the service and
* method being called.
*
* Some rules:
*
* a) An implementation **should** accept default `RpcOptions` (or an
* interface that extends `RpcOptions`) in the constructor.
*
* b) An implementation **must** merge the options given to `mergeOptions()`
* with its default options. If no extra options are implemented, or only
* primitive option values are used, using `mergeRpcOptions()` will
* produce the required behaviour.
*
* c) An implementation **must** pass `RpcOptions.jsonOptions` and
* `RpcOptions.binaryOptions` to the `fromBinary`, `toBinary`, `fromJson`
* and `toJson` methods when preparing a request or parsing a response.
*
* d) An implementation may support arbitrary other options, but they **must
* not** interfere with options keys of the binary or JSON options.
*/
export interface RpcTransport {
/**
* Merge call options with default options.
* Generated service clients will call this method with the users'
* call options and pass the result to the execute-method below.
*/
mergeOptions(options?: Partial<RpcOptions>): RpcOptions;
/**
* Execute an unary RPC.
*/
unary<I extends object, O extends object>(method: MethodInfo<I, O>, input: I, options: RpcOptions): UnaryCall<I, O>;
/**
* Execute a server streaming RPC.
*/
serverStreaming<I extends object, O extends object>(method: MethodInfo<I, O>, input: I, options: RpcOptions): ServerStreamingCall<I, O>;
/**
* Execute a client streaming RPC.
*/
clientStreaming<I extends object, O extends object>(method: MethodInfo<I, O>, options: RpcOptions): ClientStreamingCall<I, O>;
/**
* Execute a duplex streaming RPC.
*/
duplex<I extends object, O extends object>(method: MethodInfo<I, O>, options: RpcOptions): DuplexStreamingCall<I, O>;
}

View file

@ -0,0 +1,101 @@
import type { MethodInfo } from "./reflection-info";
import type { RpcMetadata } from "./rpc-metadata";
import type { RpcStatus } from "./rpc-status";
declare type CancelCallback = () => void;
declare type RemoveListenerFn = () => void;
declare type SendResponseHeadersFn = (headers: RpcMetadata) => void;
export declare class ServerCallContextController implements ServerCallContext {
private _cancelled;
private readonly _sendRH;
private readonly _listeners;
constructor(method: MethodInfo, headers: Readonly<RpcMetadata>, deadline: Date, sendResponseHeadersFn: SendResponseHeadersFn, defaultStatus?: RpcStatus);
/**
* Set the call cancelled.
*
* Invokes all callbacks registered with onCancel() and
* sets `cancelled = true`.
*/
notifyCancelled(): void;
/**
* Reflection information about this call.
*/
readonly method: MethodInfo;
/**
* Request headers.
*/
readonly headers: Readonly<RpcMetadata>;
/**
* Deadline for this call.
*/
readonly deadline: Date;
/**
* Trailers to send when the response is finished.
*/
trailers: RpcMetadata;
/**
* Status to send when the response is finished.
*/
status: RpcStatus;
/**
* Send response headers.
*/
sendResponseHeaders(data: RpcMetadata): void;
/**
* Is the call cancelled?
*
* When the client closes the connection before the server
* is done, the call is cancelled.
*
* If you want to cancel a request on the server, throw a
* RpcError with the CANCELLED status code.
*/
get cancelled(): boolean;
/**
* Add a callback for cancellation.
*/
onCancel(callback: CancelCallback): RemoveListenerFn;
}
/**
* Context for a RPC call on the server side.
*/
export interface ServerCallContext {
/**
* Reflection information about this call.
*/
readonly method: MethodInfo;
/**
* Request headers.
*/
readonly headers: Readonly<RpcMetadata>;
/**
* Deadline for this call.
*/
readonly deadline: Date;
/**
* Trailers to send when the response is finished.
*/
trailers: RpcMetadata;
/**
* Status to send when the response is finished.
*/
status: RpcStatus;
/**
* Send response headers.
*/
sendResponseHeaders(data: RpcMetadata): void;
/**
* Is the call cancelled?
*
* When the client closes the connection before the server
* is done, the call is cancelled.
*
* If you want to cancel a request on the server, throw a
* RpcError with the CANCELLED status code.
*/
readonly cancelled: boolean;
/**
* Add a callback for cancellation.
*/
onCancel(cb: CancelCallback): RemoveListenerFn;
}
export {};

View file

@ -0,0 +1,98 @@
import type { RpcCallShared } from "./rpc-call-shared";
import type { RpcOutputStream } from "./rpc-output-stream";
import type { RpcStatus } from "./rpc-status";
import type { MethodInfo } from "./reflection-info";
import type { RpcMetadata } from "./rpc-metadata";
/**
* A server streaming RPC call. The client provides exactly one input message
* but the server may respond with 0, 1, or more messages.
*/
export declare class ServerStreamingCall<I extends object = object, O extends object = object> implements RpcCallShared<I, O>, PromiseLike<FinishedServerStreamingCall<I, O>> {
/**
* Reflection information about this call.
*/
readonly method: MethodInfo<I, O>;
/**
* Request headers being sent with the request.
*
* Request headers are provided in the `meta` property of the
* `RpcOptions` passed to a call.
*/
readonly requestHeaders: Readonly<RpcMetadata>;
/**
* The request message being sent.
*/
readonly request: Readonly<I>;
/**
* The response headers that the server sent.
*
* This promise will reject with a `RpcError` when the server sends a
* error status code.
*/
readonly headers: Promise<RpcMetadata>;
/**
* Response messages from the server.
* This is an AsyncIterable that can be iterated with `await for .. of`.
*/
readonly responses: RpcOutputStream<O>;
/**
* The response status the server replied with.
*
* This promise will resolve when the server has finished the request
* successfully.
*
* If the server replies with an error status, this promise will
* reject with a `RpcError`.
*/
readonly status: Promise<RpcStatus>;
/**
* The trailers the server attached to the response.
*
* This promise will resolve when the server has finished the request
* successfully.
*
* If the server replies with an error status, this promise will
* reject with a `RpcError`.
*/
readonly trailers: Promise<RpcMetadata>;
constructor(method: MethodInfo<I, O>, requestHeaders: Readonly<RpcMetadata>, request: Readonly<I>, headers: Promise<RpcMetadata>, response: RpcOutputStream<O>, status: Promise<RpcStatus>, trailers: Promise<RpcMetadata>);
/**
* Instead of awaiting the response status and trailers, you can
* just as well await this call itself to receive the server outcome.
* You should first setup some listeners to the `request` to
* see the actual messages the server replied with.
*/
then<TResult1 = FinishedServerStreamingCall<I, O>, TResult2 = never>(onfulfilled?: ((value: FinishedServerStreamingCall<I, O>) => (PromiseLike<TResult1> | TResult1)) | undefined | null, onrejected?: ((reason: any) => (PromiseLike<TResult2> | TResult2)) | undefined | null): Promise<TResult1 | TResult2>;
private promiseFinished;
}
/**
* A completed server streaming RPC call. The server will not send any more
* messages.
*/
export interface FinishedServerStreamingCall<I extends object, O extends object> {
/**
* Reflection information about this call.
*/
readonly method: MethodInfo<I, O>;
/**
* Request headers being sent with the request.
*/
readonly requestHeaders: Readonly<RpcMetadata>;
/**
* The request message being sent.
*/
readonly request: Readonly<I>;
/**
* The response headers that the server sent.
*/
readonly headers: RpcMetadata;
/**
* The response status the server replied with.
* The status code will always be OK.
*/
readonly status: RpcStatus;
/**
* The trailers the server attached to the response.
*/
readonly trailers: RpcMetadata;
}

View file

@ -0,0 +1,23 @@
import { MethodInfo, PartialMethodInfo, ServiceInfo } from "./reflection-info";
import type { JsonValue } from "@protobuf-ts/runtime";
export declare class ServiceType implements ServiceInfo {
/**
* The protobuf type name of the service, including package name if
* present.
*/
readonly typeName: string;
/**
* Information for each rpc method of the service, in the order of
* declaration in the source .proto.
*/
readonly methods: MethodInfo[];
/**
* Contains custom service options from the .proto source in JSON format.
*/
readonly options: JsonOptionsMap;
constructor(typeName: string, methods: PartialMethodInfo[], options?: JsonOptionsMap);
}
declare type JsonOptionsMap = {
[extensionName: string]: JsonValue;
};
export {};

View file

@ -0,0 +1,101 @@
import { RpcError } from "./rpc-error";
import type { RpcMetadata } from "./rpc-metadata";
import type { RpcStatus } from "./rpc-status";
import type { RpcTransport } from "./rpc-transport";
import type { MethodInfo } from "./reflection-info";
import { RpcOptions } from "./rpc-options";
import { UnaryCall } from "./unary-call";
import { ServerStreamingCall } from "./server-streaming-call";
import { ClientStreamingCall } from "./client-streaming-call";
import { DuplexStreamingCall } from "./duplex-streaming-call";
/**
* Mock data for the TestTransport.
*/
interface TestTransportMockData {
/**
* Input stream behaviour for client streaming and bidi calls.
* If RpcError, sending a message rejects with this error.
* If number, sending message is delayed for N milliseconds.
* If omitted, sending a message is delayed for 10 milliseconds.
*/
inputMessage?: RpcError | number;
/**
* Input stream behaviour for client streaming and bidi calls.
* If RpcError, completing the stream rejects with this error.
* If number, completing the stream is delayed for N milliseconds.
* If omitted, completing the stream is delayed for 10 milliseconds.
*/
inputComplete?: RpcError | number;
/**
* If not provided, defaults to `{ responseHeader: "test" }`
* If RpcError, the "headers" promise is rejected with this error.
*/
headers?: RpcMetadata | RpcError;
/**
* If not provided, transport creates default output message using method info
* If RpcError, the "response" promise / stream is rejected with this error.
*/
response?: object | readonly object[] | RpcError;
/**
* If not provided, defaults to `{ code: "OK", detail: "all good" }`
* If RpcError, the "status" promise is rejected with this error.
*/
status?: RpcStatus | RpcError;
/**
* If not provided, defaults to `{ responseTrailer: "test" }`
* If RpcError, the "trailers" promise is rejected with this error.
*/
trailers?: RpcMetadata | RpcError;
}
/**
* Transport for testing.
*/
export declare class TestTransport implements RpcTransport {
static readonly defaultHeaders: Readonly<RpcMetadata>;
static readonly defaultStatus: Readonly<RpcStatus>;
static readonly defaultTrailers: Readonly<RpcMetadata>;
/**
* Sent message(s) during the last operation.
*/
get sentMessages(): any[];
/**
* Sending message(s) completed?
*/
get sendComplete(): boolean;
/**
* Suppress warning / error about uncaught rejections of
* "status" and "trailers".
*/
suppressUncaughtRejections: boolean;
private readonly data;
private readonly headerDelay;
private readonly responseDelay;
private readonly betweenResponseDelay;
private readonly afterResponseDelay;
private lastInput;
/**
* Initialize with mock data. Omitted fields have default value.
*/
constructor(data?: TestTransportMockData);
private promiseHeaders;
private promiseSingleResponse;
/**
* Pushes response messages from the mock data to the output stream.
* If an error response, status or trailers are mocked, the stream is
* closed with the respective error.
* Otherwise, stream is completed successfully.
*
* The returned promise resolves when the stream is closed. It should
* not reject. If it does, code is broken.
*/
private streamResponses;
private promiseStatus;
private promiseTrailers;
private maybeSuppressUncaught;
mergeOptions(options?: Partial<RpcOptions>): RpcOptions;
unary<I extends object, O extends object>(method: MethodInfo<I, O>, input: I, options: RpcOptions): UnaryCall<I, O>;
serverStreaming<I extends object, O extends object>(method: MethodInfo<I, O>, input: I, options: RpcOptions): ServerStreamingCall<I, O>;
clientStreaming<I extends object, O extends object>(method: MethodInfo<I, O>, options: RpcOptions): ClientStreamingCall<I, O>;
duplex<I extends object, O extends object>(method: MethodInfo<I, O>, options: RpcOptions): DuplexStreamingCall<I, O>;
}
export {};

View file

@ -0,0 +1,101 @@
import type { RpcCallShared } from "./rpc-call-shared";
import type { RpcStatus } from "./rpc-status";
import type { MethodInfo } from "./reflection-info";
import type { RpcMetadata } from "./rpc-metadata";
/**
* A unary RPC call. Unary means there is exactly one input message and
* exactly one output message unless an error occurred.
*/
export declare class UnaryCall<I extends object = object, O extends object = object> implements RpcCallShared<I, O>, PromiseLike<FinishedUnaryCall<I, O>> {
/**
* Reflection information about this call.
*/
readonly method: MethodInfo<I, O>;
/**
* Request headers being sent with the request.
*
* Request headers are provided in the `meta` property of the
* `RpcOptions` passed to a call.
*/
readonly requestHeaders: Readonly<RpcMetadata>;
/**
* The request message being sent.
*/
readonly request: Readonly<I>;
/**
* The response headers that the server sent.
*
* This promise will reject with a `RpcError` when the server sends an
* error status code.
*/
readonly headers: Promise<RpcMetadata>;
/**
* The message the server replied with.
*
* If the server does not send a message, this promise will reject with a
* `RpcError`.
*/
readonly response: Promise<O>;
/**
* The response status the server replied with.
*
* This promise will resolve when the server has finished the request
* successfully.
*
* If the server replies with an error status, this promise will
* reject with a `RpcError`.
*/
readonly status: Promise<RpcStatus>;
/**
* The trailers the server attached to the response.
*
* This promise will resolve when the server has finished the request
* successfully.
*
* If the server replies with an error status, this promise will
* reject with a `RpcError`.
*/
readonly trailers: Promise<RpcMetadata>;
constructor(method: MethodInfo<I, O>, requestHeaders: RpcMetadata, request: I, headers: Promise<RpcMetadata>, response: Promise<O>, status: Promise<RpcStatus>, trailers: Promise<RpcMetadata>);
/**
* If you are only interested in the final outcome of this call,
* you can await it to receive a `FinishedUnaryCall`.
*/
then<TResult1 = FinishedUnaryCall<I, O>, TResult2 = never>(onfulfilled?: ((value: FinishedUnaryCall<I, O>) => (PromiseLike<TResult1> | TResult1)) | undefined | null, onrejected?: ((reason: any) => (PromiseLike<TResult2> | TResult2)) | undefined | null): Promise<TResult1 | TResult2>;
private promiseFinished;
}
/**
* A completed unary RPC call. This will only exists if the RPC was
* successful.
*/
export interface FinishedUnaryCall<I extends object, O extends object> {
/**
* Reflection information about this call.
*/
readonly method: MethodInfo<I, O>;
/**
* Request headers being sent with the request.
*/
readonly requestHeaders: Readonly<RpcMetadata>;
/**
* The request message that has been sent.
*/
readonly request: Readonly<I>;
/**
* The response headers that the server sent.
*/
readonly headers: RpcMetadata;
/**
* The message the server replied with.
*/
readonly response: O;
/**
* The response status the server replied with.
* The status code will always be OK.
*/
readonly status: RpcStatus;
/**
* The trailers the server attached to the response.
*/
readonly trailers: RpcMetadata;
}