UNPKG

12.9 kBSource Map (JSON)View Raw
1{"version":3,"file":"full.min.mjs","sources":["../src/utils/error.ts","../src/core/notify.ts","../src/Async-Call-Generator.ts"],"sourcesContent":["class CustomError extends Error {\n constructor(public name: string, message: string, public code: number, public stack: string) {\n super(message)\n }\n}\nexport const Err_Cannot_find_a_running_iterator_with_given_ID = {} as Symbol\nexport const Err_Only_string_can_be_the_RPC_method_name = {} as Symbol\nexport const Err_Cannot_call_method_starts_with_rpc_dot_directly = {} as Symbol\nexport const Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options = {} as Symbol\nconst Messages = [\n Err_Cannot_find_a_running_iterator_with_given_ID,\n Err_Only_string_can_be_the_RPC_method_name,\n Err_Cannot_call_method_starts_with_rpc_dot_directly,\n Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options,\n]\n// https://github.com/Jack-Works/async-call-rpc/wiki/Error-messages\nexport function makeHostedMessage(id: Symbol, error: Error) {\n const n = Messages.indexOf(id)\n error.message += `Error ${n}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#` + n\n return error\n}\n// ! side effect\n/** These Error is defined in ECMAScript spec */\nconst errors: Record<string, typeof EvalError> = {\n // @ts-ignore\n __proto__: null,\n Error,\n EvalError,\n RangeError,\n ReferenceError,\n SyntaxError,\n TypeError,\n URIError,\n}\nexport const DOMExceptionHeader = 'DOMException:'\n/**\n * AsyncCall support somehow transfer ECMAScript Error\n */\nexport const RecoverError = (type: string, message: string, code: number, stack: string): Error => {\n try {\n const E = globalDOMException()\n if (type.startsWith(DOMExceptionHeader) && E) {\n const name = type.slice(DOMExceptionHeader.length)\n return new E(message, name)\n } else if (type in errors) {\n const e = new errors[type]!(message)\n e.stack = stack\n // @ts-ignore\n e.code = code\n return e\n } else {\n return new CustomError(type, message, code, stack)\n }\n } catch {\n return new Error(`E${code} ${type}: ${message}\\n${stack}`)\n }\n}\nexport const removeStackHeader = (stack: unknown) => String(stack).replace(/^.+\\n.+\\n/, '')\n// ! side effect\nexport const globalDOMException = (() => {\n try {\n // @ts-ignore\n return DOMException\n } catch {}\n}) as () => DOMException | undefined\ntype DOMException = { new (message: string, name: string): any }\n","import { AsyncCallNotify } from '../utils/internalSymbol'\nimport { isFunction } from '../utils/constants'\n\n/**\n * Make the returning type to `Promise<void>`\n * @internal\n * @remarks\n * Due to the limitation of TypeScript, generic signatures cannot be preserved\n * if the function is the top level parameter of this utility type,\n * or the function is not returning `Promise<void>`.\n */\nexport type _IgnoreResponse<T> = T extends (...args: infer Args) => unknown\n ? (...args: Args) => Promise<void>\n : {\n [key in keyof T as T[key] extends Function ? key : never]: T[key] extends (\n ...args: infer Args\n ) => infer Return\n ? Return extends Promise<void>\n ? T[key]\n : (...args: Args) => Promise<void>\n : never\n }\n/**\n * Wrap the AsyncCall instance to send notification.\n * @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance\n * @example\n * const notifyOnly = notify(AsyncCall(...))\n * @public\n */\n\nexport function notify<T extends object>(instanceOrFnOnInstance: T): _IgnoreResponse<T> {\n if (isFunction(instanceOrFnOnInstance)) return (instanceOrFnOnInstance as any)[AsyncCallNotify]\n return new Proxy(instanceOrFnOnInstance, { get: notifyTrap }) as any\n}\nconst notifyTrap = (target: any, p: string | number | symbol) => {\n return target[p][AsyncCallNotify]\n}\n","/**\n * See the document at https://github.com/Jack-Works/async-call/\n */\nimport { AsyncCallOptions, AsyncCall } from './Async-Call'\nimport { AsyncCallIgnoreResponse } from './utils/internalSymbol'\nimport { normalizeStrictOptions } from './utils/normalizeOptions'\nimport { generateRandomID } from './utils/generateRandomID'\nimport { isFunction, isString, Object_setPrototypeOf, Promise_resolve } from './utils/constants'\nimport {\n Err_Cannot_find_a_running_iterator_with_given_ID,\n Err_Only_string_can_be_the_RPC_method_name,\n makeHostedMessage,\n} from './utils/error'\n\nconst i = 'rpc.async-iterator.'\n// ! side effect\nconst AsyncIteratorStart = Symbol.for(i + 'start')\nconst AsyncIteratorNext = Symbol.for(i + 'next')\nconst AsyncIteratorReturn = Symbol.for(i + 'return')\nconst AsyncIteratorThrow = Symbol.for(i + 'throw')\n\ninterface AsyncGeneratorInternalMethods {\n [AsyncIteratorStart](method: string, params: unknown[]): Promise<string>\n [AsyncIteratorNext](id: string, value: unknown): Promise<IteratorResult<unknown>>\n [AsyncIteratorReturn](id: string, value: unknown): Promise<IteratorResult<unknown>>\n [AsyncIteratorThrow](id: string, value: unknown): Promise<IteratorResult<unknown>>\n}\n\n/** @internal */\nexport type _IteratorOrIterableFunction = (\n ...args: any\n) => Iterator<any, any, any> | Iterable<any> | AsyncIterator<any, any, any> | AsyncIterable<any>\n/** @internal */\nexport type _IteratorLikeToAsyncGenerator<T extends _IteratorOrIterableFunction> = T extends (\n ...args: any\n) => AsyncGenerator<any>\n ? T // return async generator as-is so generics can be preserved\n : T extends (\n ...args: infer Args\n ) =>\n | Iterator<infer Yield, infer Return, infer Next>\n | Iterable<infer Yield>\n | AsyncIterator<infer Yield, infer Return, infer Next>\n | AsyncIterable<infer Yield>\n ? (...args: Args) => AsyncGenerator<Yield, Return, Next>\n : never\n\n/**\n * Make all generator in the type T becomes AsyncGenerator\n *\n * @remarks\n * Only generics signatures on function that returning an AsyncGenerator<T> will be preserved due to the limitation of TypeScript.\n *\n * Method called `then` are intentionally removed because it is very likely to be a foot gun in promise auto-unwrap.\n * @internal\n */\nexport type _AsyncGeneratorVersionOf<T> = {\n // Omit 'then'\n [key in keyof T as key extends 'then'\n ? never\n : // Omit non-iterator/iterable\n T[key] extends _IteratorOrIterableFunction\n ? key\n : never]: T[key] extends _IteratorOrIterableFunction ? _IteratorLikeToAsyncGenerator<T[key]> : never\n}\n\ntype Iter = Iterator<unknown, unknown, unknown> | AsyncIterator<unknown>\ntype IterResult = IteratorResult<unknown> | Promise<IteratorResult<unknown>>\n/**\n * The async generator version of the AsyncCall\n * @param thisSideImplementation - The implementation when this AsyncCall acts as a JSON RPC server.\n * @param options - {@link AsyncCallOptions}\n * @typeParam OtherSideImplementedFunctions - The type of the API that server expose. For any function on this interface, AsyncCall will convert it to the Promised type.\n * @remarks\n * Warning: Due to technical limitation, AsyncGeneratorCall will leak memory. Use it at your own risk.\n *\n * To use AsyncGeneratorCall, the server and the client MUST support the following JSON RPC internal methods which is pre ECMAScript async generator semantics:\n *\n * - `rpc.async-iterator.start`\n *\n * - `rpc.async-iterator.next`\n *\n * - `rpc.async-iterator.return`\n *\n * - `rpc.async-iterator.throw`\n *\n * @example\n * ```ts\n * const server = {\n * async *generator() {\n * let last = 0\n * while (true) yield last++\n * },\n * }\n * type Server = typeof server\n * const serverRPC = AsyncGeneratorCall<Server>({}, { channel })\n * async function main() {\n * for await (const x of serverRPC.generator()) {\n * console.log('Server yielded number', x)\n * }\n * }\n * ```\n * @public\n */\nexport function AsyncGeneratorCall<OtherSideImplementedFunctions = {}>(\n thisSideImplementation: null | undefined | object | Promise<object>,\n options: AsyncCallOptions,\n): _AsyncGeneratorVersionOf<OtherSideImplementedFunctions> {\n const iterators = new Map<string | number, Iter>()\n const [methodNotFound] = normalizeStrictOptions(options.strict ?? true)\n const { idGenerator = generateRandomID } = options\n const findIterator = (\n id: string,\n next: (iterator: Iter) => IterResult | undefined | false,\n ): false | undefined | IterResult | typeof AsyncCallIgnoreResponse => {\n const it = iterators.get(id)\n if (!it) {\n if (methodNotFound)\n throw makeHostedMessage(Err_Cannot_find_a_running_iterator_with_given_ID, new Error(`Iterator ${id}, `))\n else return AsyncCallIgnoreResponse\n }\n const result = next(it)\n isFinished(result, () => iterators.delete(id))\n return result\n }\n const server = {\n async [AsyncIteratorStart](method, args) {\n const iteratorGenerator: unknown = ((await thisSideImplementation) as any)[method]\n if (!isFunction(iteratorGenerator)) {\n if (methodNotFound) throw new TypeError(method + ' is not a function')\n else return AsyncCallIgnoreResponse\n }\n const iterator = iteratorGenerator(...args)\n const id = idGenerator()\n iterators.set(id, iterator)\n return Promise_resolve(id)\n },\n [AsyncIteratorNext](id, val) {\n return findIterator(id, (it) => it.next(val as any))\n },\n [AsyncIteratorReturn](id, val) {\n return findIterator(id, (it) => isFunction(it.return) && it.return(val))\n },\n [AsyncIteratorThrow](id, val) {\n return findIterator(id, (it) => isFunction(it.throw) && it.throw(val))\n },\n } as AsyncGeneratorInternalMethods\n const remote = AsyncCall<AsyncGeneratorInternalMethods>(server, options)\n const proxyTrap = (cache: any, key: string): ((...args: unknown[]) => AsyncIterableIterator<unknown>) => {\n if (!isString(key))\n throw makeHostedMessage(Err_Only_string_can_be_the_RPC_method_name, new TypeError(''))\n if (cache[key]) return cache[key]\n const f = (...args: unknown[]) => {\n const id = remote[AsyncIteratorStart](key, args)\n return new _AsyncGenerator(remote, id)\n }\n Object.defineProperty(cache, key, { value: f, configurable: true })\n return f\n }\n return new Proxy({ __proto__: null }, { get: proxyTrap }) as _AsyncGeneratorVersionOf<OtherSideImplementedFunctions>\n}\nclass _AsyncGenerator implements AsyncIterableIterator<unknown>, AsyncIterator<unknown, unknown, unknown> {\n /** done? */\n private d: boolean = false\n /** check */\n private c = async (val: IterResult) => {\n await isFinished(val, () => (this.d = true))\n return val\n }\n /**\n * @param r Remote Implementation\n * @param i id\n */\n constructor(private r: AsyncGeneratorInternalMethods, private i: Promise<string>) {}\n async return(val: unknown) {\n if (this.d) return makeIteratorResult(true, val)\n return this.c(this.r[AsyncIteratorReturn](await this.i, val))\n }\n async next(val?: unknown) {\n if (this.d) return makeIteratorResult(true)\n return this.c(this.r[AsyncIteratorNext](await this.i, val))\n }\n async throw(val?: unknown) {\n if (!this.d) return this.c(this.r[AsyncIteratorThrow](await this.i, val))\n throw val\n }\n // Inherited from AsyncGeneratorPrototype\n declare [Symbol.asyncIterator]: () => this\n}\n// ! side effect\nconst EmptyAsyncGenerator = async function* () {}\nconst AsyncGeneratorConstructor = EmptyAsyncGenerator.constructor\nconst AsyncGeneratorConstructorPrototype = AsyncGeneratorConstructor.prototype\nObject_setPrototypeOf(_AsyncGenerator, AsyncGeneratorConstructorPrototype)\nconst AsyncGeneratorPrototype = Object.getPrototypeOf(EmptyAsyncGenerator())\nObject_setPrototypeOf(_AsyncGenerator.prototype, AsyncGeneratorPrototype)\n\nconst isFinished = async (result: IterResult | undefined | false, cb: () => void) => {\n try {\n const x = await result\n x && x.done && cb()\n } catch {}\n}\n\nconst makeIteratorResult = (done: boolean, value: unknown = undefined): IteratorResult<unknown, unknown> => ({\n done,\n value,\n})\n"],"names":["Object_setPrototypeOf"],"mappings":";AAAA,mQAuBA,msDCWA,opJCpBA,m1BAmJA,4YA6BA,4BAGAA,6BACA,mCACAA,iBAEA"}
\No newline at end of file