|
| 1 | +import connector from './lib/connector' |
| 2 | +import noop from 'licia/noop' |
| 3 | +import uuid from 'licia/uuid' |
| 4 | +import each from 'licia/each' |
| 5 | +import Emitter from 'licia/Emitter' |
| 6 | +import { ErrorWithCode } from './lib/util' |
| 7 | +import types from 'licia/types' |
| 8 | + |
| 9 | +type OnMessage = (message: string) => void |
| 10 | +type DomainMethod = (...args: any[]) => any |
| 11 | + |
| 12 | +export default class Chobitsu { |
| 13 | + private onMessage: OnMessage |
| 14 | + private resolves: Map<string, (value?: any) => void> = new Map() |
| 15 | + private domains: Map<string, { [index: string]: DomainMethod }> = new Map() |
| 16 | + constructor() { |
| 17 | + this.onMessage = noop |
| 18 | + connector.on('message', (message: any) => { |
| 19 | + const parsedMessage = JSON.parse(message) |
| 20 | + |
| 21 | + const resolve = this.resolves.get(parsedMessage.id) |
| 22 | + if (resolve) { |
| 23 | + resolve(parsedMessage.result) |
| 24 | + } |
| 25 | + |
| 26 | + if (!parsedMessage.id) { |
| 27 | + const [name, method] = parsedMessage.method.split('.') |
| 28 | + const domain = this.domains.get(name) |
| 29 | + if (domain) { |
| 30 | + domain.emit(method, parsedMessage.params) |
| 31 | + } |
| 32 | + } |
| 33 | + |
| 34 | + this.onMessage(message) |
| 35 | + }) |
| 36 | + } |
| 37 | + domain(name: string) { |
| 38 | + return this.domains.get(name) |
| 39 | + } |
| 40 | + setOnMessage(onMessage: OnMessage) { |
| 41 | + this.onMessage = onMessage |
| 42 | + } |
| 43 | + sendMessage(method: string, params: any = {}) { |
| 44 | + const id = uuid() |
| 45 | + |
| 46 | + this.sendRawMessage( |
| 47 | + JSON.stringify({ |
| 48 | + id, |
| 49 | + method, |
| 50 | + params, |
| 51 | + }) |
| 52 | + ) |
| 53 | + |
| 54 | + return new Promise(resolve => { |
| 55 | + this.resolves.set(id, resolve) |
| 56 | + }) |
| 57 | + } |
| 58 | + async sendRawMessage(message: string) { |
| 59 | + const parsedMessage = JSON.parse(message) |
| 60 | + |
| 61 | + const { method, params, id } = parsedMessage |
| 62 | + |
| 63 | + const resultMsg: any = { |
| 64 | + id, |
| 65 | + } |
| 66 | + |
| 67 | + try { |
| 68 | + resultMsg.result = await this.callMethod(method, params) |
| 69 | + } catch (e) { |
| 70 | + if (e instanceof ErrorWithCode) { |
| 71 | + resultMsg.error = { |
| 72 | + message: e.message, |
| 73 | + code: e.code, |
| 74 | + } |
| 75 | + } else if (e instanceof Error) { |
| 76 | + resultMsg.error = { |
| 77 | + message: e.message, |
| 78 | + } |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + connector.emit('message', JSON.stringify(resultMsg)) |
| 83 | + } |
| 84 | + register(name: string, methods: types.PlainObj<types.AnyFn>) { |
| 85 | + const domains = this.domains |
| 86 | + |
| 87 | + let domain = domains.get(name)! |
| 88 | + if (!domain) { |
| 89 | + domain = {} |
| 90 | + Emitter.mixin(domain) |
| 91 | + } |
| 92 | + each(methods, (fn: any, method: string) => { |
| 93 | + domain[method] = fn |
| 94 | + }) |
| 95 | + domains.set(name, domain) |
| 96 | + } |
| 97 | + private async callMethod(method: string, params: any) { |
| 98 | + const [ domainName, methodName ] = method.split('.') |
| 99 | + const domain = this.domain(domainName) |
| 100 | + if (domain) { |
| 101 | + if (domain[methodName]) { |
| 102 | + return domain[methodName](params) || {} |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + throw Error(`${method} unimplemented`) |
| 107 | + } |
| 108 | +} |
0 commit comments