UNPKG

@protobuf-ts/twirp-transport

Version:

Twirp transport for clients generated by the protoc plugin "protobuf-ts".

131 lines (130 loc) 7.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TwirpFetchTransport = void 0; const twirp_format_1 = require("./twirp-format"); const runtime_rpc_1 = require("@protobuf-ts/runtime-rpc"); const runtime_1 = require("@protobuf-ts/runtime"); const twitch_twirp_error_code_1 = require("./twitch-twirp-error-code"); /** * Implements the Twirp protocol, supporting JSON or binary format on * the wire. Uses the fetch API to do the HTTP requests. * * See https://twitchtv.github.io/twirp/docs/spec_v5.html */ class TwirpFetchTransport { constructor(options) { this.defaultOptions = options; } mergeOptions(options) { return runtime_rpc_1.mergeRpcOptions(this.defaultOptions, options); } unary(method, input, options) { var _a, _b, _c; let opt = options, url = this.makeUrl(method, opt), fetchInit = (_a = opt.fetchInit) !== null && _a !== void 0 ? _a : {}, requestBody = opt.sendJson ? method.I.toJsonString(input, opt.jsonOptions) : method.I.toBinary(input, opt.binaryOptions), defHeader = new runtime_rpc_1.Deferred(), defMessage = new runtime_rpc_1.Deferred(), defStatus = new runtime_rpc_1.Deferred(), defTrailer = new runtime_rpc_1.Deferred(); globalThis.fetch(url, Object.assign(Object.assign({}, fetchInit), { method: 'POST', headers: twirp_format_1.createTwirpRequestHeader(new globalThis.Headers(), !!opt.sendJson, opt.meta), body: requestBody, signal: (_b = options.abort) !== null && _b !== void 0 ? _b : null // node-fetch@3.0.0-beta.9 rejects `undefined` })) .then(fetchResponse => { defHeader.resolve(twirp_format_1.parseMetadataFromResponseHeaders(fetchResponse.headers)); // Cloudflare Workers throw when the type property of a fetch response // is accessed, so wrap access with try/catch. See: // * https://developers.cloudflare.com/workers/runtime-apis/response/#properties // * https://github.com/cloudflare/miniflare/blob/72f046e/packages/core/src/standards/http.ts#L646 let responseType; try { responseType = fetchResponse.type; } catch (_a) { } switch (responseType) { case "error": case "opaque": case "opaqueredirect": // see https://developer.mozilla.org/en-US/docs/Web/API/Response/type throw new runtime_rpc_1.RpcError(`fetch response type ${fetchResponse.type}`, twitch_twirp_error_code_1.TwirpErrorCode[twitch_twirp_error_code_1.TwirpErrorCode.unknown]); } if (!fetchResponse.ok) { return fetchResponse.json().then(value => { throw twirp_format_1.parseTwirpErrorResponse(value); }, () => { throw new runtime_rpc_1.RpcError('received HTTP ' + fetchResponse.status + ', unable to read response body as json', twitch_twirp_error_code_1.TwirpErrorCode[twitch_twirp_error_code_1.TwirpErrorCode.internal]); }); } if (opt.sendJson) { return fetchResponse.json().then(value => method.O.fromJson(value, opt.jsonOptions), () => { throw new runtime_rpc_1.RpcError('unable to read response body as json', twitch_twirp_error_code_1.TwirpErrorCode[twitch_twirp_error_code_1.TwirpErrorCode.dataloss]); }); } return fetchResponse.arrayBuffer().then(value => method.O.fromBinary(new Uint8Array(value), opt.binaryOptions), () => { throw new runtime_rpc_1.RpcError('unable to read response body', twitch_twirp_error_code_1.TwirpErrorCode[twitch_twirp_error_code_1.TwirpErrorCode.dataloss]); }); }, (reason) => { // failed to fetch, aborted, wrong url or network problem if (reason instanceof Error && reason.name === 'AbortError') throw new runtime_rpc_1.RpcError(reason.message, twitch_twirp_error_code_1.TwirpErrorCode[twitch_twirp_error_code_1.TwirpErrorCode.cancelled]); throw new runtime_rpc_1.RpcError(reason instanceof Error ? reason.message : reason); }) .then(message => { defMessage.resolve(message); defStatus.resolve({ code: 'OK', detail: '' }); defTrailer.resolve({}); }) .catch((reason) => { // RpcErrors are thrown by us, everything else is an internal error let error = reason instanceof runtime_rpc_1.RpcError ? reason : new runtime_rpc_1.RpcError(reason instanceof Error ? reason.message : reason, twitch_twirp_error_code_1.TwirpErrorCode[twitch_twirp_error_code_1.TwirpErrorCode.internal]); error.methodName = method.name; error.serviceName = method.service.typeName; defHeader.rejectPending(error); defMessage.rejectPending(error); defStatus.rejectPending(error); defTrailer.rejectPending(error); }); return new runtime_rpc_1.UnaryCall(method, (_c = opt.meta) !== null && _c !== void 0 ? _c : {}, input, defHeader.promise, defMessage.promise, defStatus.promise, defTrailer.promise); } /** * Create an URI for a RPC call. * * Takes the `baseUrl` option and appends: * - slash "/" * - package name * - dot "." * - service name * - slash "/" * - method name * * If the service was declared without a package, the package name and dot * are omitted. * * The method name is CamelCased just as it would be in Go, unless the * option `useProtoMethodName` is `true`. */ makeUrl(method, options) { let base = options.baseUrl; if (base.endsWith('/')) base = base.substring(0, base.length - 1); let methodName = method.name; if (options.useProtoMethodName !== true) { methodName = runtime_1.lowerCamelCase(methodName); methodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1); } return `${base}/${method.service.typeName}/${methodName}`; } clientStreaming(method /*, options: RpcOptions*/) { const e = new runtime_rpc_1.RpcError('Client streaming is not supported by Twirp', twitch_twirp_error_code_1.TwirpErrorCode[twitch_twirp_error_code_1.TwirpErrorCode.unimplemented]); e.methodName = method.name; e.serviceName = method.service.typeName; throw e; } duplex(method /*, options: RpcOptions*/) { const e = new runtime_rpc_1.RpcError('Duplex streaming is not supported by Twirp', twitch_twirp_error_code_1.TwirpErrorCode[twitch_twirp_error_code_1.TwirpErrorCode.unimplemented]); e.methodName = method.name; e.serviceName = method.service.typeName; throw e; } serverStreaming(method /*, input: I, options?: RpcOptions*/) { const e = new runtime_rpc_1.RpcError('Server streaming is not supported by Twirp', twitch_twirp_error_code_1.TwirpErrorCode[twitch_twirp_error_code_1.TwirpErrorCode.unimplemented]); e.methodName = method.name; e.serviceName = method.service.typeName; throw e; } } exports.TwirpFetchTransport = TwirpFetchTransport;