Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | import type { Errors, IErrorFactory, IErrorMap, ILocal, ILog, IMeta, IPlatformApi, } from '@feasibleone/blong/types'; import ky from 'ky'; import Remote from './Remote.ts'; export interface IGatewayClient { start: () => Promise<IGatewayClient>; stop: () => Promise<IGatewayClient>; } interface IError extends Error { type?: string; req?: object; res?: object; } const errorMap: IErrorMap = { 'gw.notFound': 'Local method "{method}" not found', 'gw.jsonRpcEmpty': 'JSON RPC response without response and error', 'gw.jsonRpcHttp': 'JSON RPC returned HTTP error {code}', }; interface IConfig { logLevel?: Parameters<ILog['logger']>[0]; url: string; debug: boolean; latency: number; } export default class GatewayClientImpl extends Remote implements IGatewayClient { #errors: Errors<typeof errorMap>; #config: IConfig = { url: 'http://localhost:8080/rpc', debug: false, latency: 100, }; public constructor( config: IConfig, { log, error, local, platform, }: {log: ILog; error: IErrorFactory; local: ILocal; platform: IPlatformApi}, ) { super(config, {log, error, local, platform}); this.merge(this.#config, config); this.#errors = error.register(errorMap); } public gateway(..._args: unknown[]): void {} protected sender(): (...params: unknown[]) => Promise<unknown> { return async (...rest) => { const {stream, ...$meta} = rest.pop() as IMeta; const params = rest; const {$http: {method: httpMethod = 'POST'} = {}} = (params?.[0] || {}) as { $http?: {method?: string}; }; const {headers, method} = $meta; const sendRequest = async (): Promise<unknown> => { const url = new URL(method!.split('.').join('/'), this.#config.url); const response = await ky(url, { // https: this.#https, // followRedirect: false, // isStream: stream, // responseType: 'json', method: httpMethod, json: { jsonrpc: '2.0', method, id: 1, ...($meta.timeout && $meta.timeout[0] && { timeout: this.spare($meta.timeout, this.#config.latency), }), params, }, headers: headers as NonNullable<Parameters<typeof ky>[1]>['headers'], }); const body = await response.json<{ jsonrpc?: string; error?: unknown; validation?: unknown; debug?: unknown; }>(); if (body?.error !== undefined) { const error: IError = body.jsonrpc ? Object.assign(new Error(), body.error) : typeof body.error === 'string' ? new Error(body.error) : Object.assign(new Error(), body.error); if (error.type) Object.defineProperty(error, 'name', { value: error.type, configurable: true, enumerable: false, }); error.req = { // httpVersion: response.httpVersion, url: url.href, method: httpMethod, ...(this.#config.debug && this.sanitize(params, $meta)), }; error.res = { // httpVersion: response.httpVersion, statusCode: response.status, }; throw error; } else if (response.status < 200 || response.status >= 300) { throw this.#errors['gw.jsonRpcHttp']({ statusCode: response.status, statusMessage: response.statusText, // httpVersion: response.httpVersion, // validation: response.body?.validation, // debug: response.body?.debug, params: { code: response.status, }, url: url.href, method: httpMethod, }); } else if (typeof body === 'object' && 'result' in body && !('error' in body)) { const result = body.result; if (/\.service\.get$/.test(method!)) { Object.assign(result as object, { protocol: url.protocol, hostname: url.hostname, port: url.port, path: url.pathname, }); } return result; } else { throw this.#errors['gw.jsonRpcEmpty'](); } }; return stream ? [sendRequest()] : sendRequest(); }; } } |