UNPKG

28.3 kBJavaScriptView Raw
1/// <reference types="./base.d.ts" />
2(function (global, factory) {
3 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4 typeof define === 'function' && define.amd ? define(['exports'], factory) :
5 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.AsyncCall = {}));
6}(this, (function (exports) { 'use strict';
7
8 class CustomError extends Error {
9 constructor( name, message, code, stack) {
10 super(message);this.name = name;this.code = code;this.stack = stack;
11 }
12 }
13 const Err_Cannot_find_a_running_iterator_with_given_ID = {};
14 const Err_Only_string_can_be_the_RPC_method_name = {};
15 const Err_Cannot_call_method_starts_with_rpc_dot_directly = {};
16 const Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options = {};
17 const Messages = [
18 Err_Cannot_find_a_running_iterator_with_given_ID,
19 Err_Only_string_can_be_the_RPC_method_name,
20 Err_Cannot_call_method_starts_with_rpc_dot_directly,
21 Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options,
22 ];
23 // https://github.com/Jack-Works/async-call-rpc/wiki/Error-messages
24 function makeHostedMessage(id, error) {
25 const n = Messages.indexOf(id);
26 error.message += `Error ${n}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#` + n;
27 return error
28 }
29 // ! side effect
30 /** These Error is defined in ECMAScript spec */
31 const errors = {
32 // @ts-ignore
33 __proto__: null,
34 Error,
35 EvalError,
36 RangeError,
37 ReferenceError,
38 SyntaxError,
39 TypeError,
40 URIError,
41 };
42 const DOMExceptionHeader = 'DOMException:';
43 /**
44 * AsyncCall support somehow transfer ECMAScript Error
45 */
46 const RecoverError = (type, message, code, stack) => {
47 try {
48 const E = globalDOMException();
49 if (type.startsWith(DOMExceptionHeader) && E) {
50 const name = type.slice(DOMExceptionHeader.length);
51 return new E(message, name)
52 } else if (type in errors) {
53 const e = new errors[type](message);
54 e.stack = stack;
55 // @ts-ignore
56 e.code = code;
57 return e
58 } else {
59 return new CustomError(type, message, code, stack)
60 }
61 } catch (e2) {
62 return new Error(`E${code} ${type}: ${message}\n${stack}`)
63 }
64 };
65 const removeStackHeader = (stack) => String(stack).replace(/^.+\n.+\n/, '');
66 // ! side effect
67 const globalDOMException = (() => {
68 try {
69 // @ts-ignore
70 return DOMException
71 } catch (e3) {}
72 });
73
74 const isString = (x) => typeof x === 'string';
75 const isBoolean = (x) => typeof x === 'boolean';
76 const isFunction = (x) => typeof x === 'function';
77 const isObject = (params) => typeof params === 'object' && params !== null;
78 const ERROR = 'Error';
79 const undefined$1 = void 0;
80 const Promise_reject = (x) => Promise.reject(x);
81 const Promise_resolve = (x) => Promise.resolve(x);
82 const isArray = Array.isArray;
83 const replayFunction = () => '() => replay()';
84
85 const jsonrpc = '2.0';
86
87
88
89
90
91
92
93
94
95
96
97
98
99 const Request = (id, method, params, remoteStack) => {
100 const x = { jsonrpc, id, method, params, remoteStack };
101 deleteUndefined(x, 'id');
102 deleteFalsy(x, 'remoteStack');
103 return x
104 };
105
106 /**
107 * JSONRPC SuccessResponse object.
108 */
109
110
111
112
113
114
115 const SuccessResponse = (id, result) => {
116 const x = { jsonrpc, id, result };
117 deleteUndefined(x, 'id');
118 return x
119 };
120
121 /**
122 * JSONRPC ErrorResponse object.
123 * @public
124 */
125
126
127
128
129
130
131
132 const ErrorResponse = (id, code, message, data) => {
133 if (id === undefined$1) id = null;
134 code = Math.floor(code);
135 if (Number.isNaN(code)) code = -1;
136 const x = { jsonrpc, id, error: { code, message, data } };
137 deleteUndefined(x.error, 'data');
138 return x
139 };
140 // Pre defined error in section 5.1
141 // ! side effect
142 const ErrorResponseParseError = (e, mapper) => {
143 const obj = ErrorResponseMapped({} , e, mapper);
144 const o = obj.error;
145 o.code = -32700;
146 o.message = 'Parse error';
147 return obj
148 };
149
150 // Not using.
151 // InvalidParams -32602 'Invalid params'
152 // InternalError -32603 'Internal error'
153 const ErrorResponseInvalidRequest = (id) => ErrorResponse(id, -32600, 'Invalid Request');
154 const ErrorResponseMethodNotFound = (id) => ErrorResponse(id, -32601, 'Method not found');
155
156
157
158
159
160 const ErrorResponseMapped = (request, e, mapper) => {
161 const { id } = request;
162 const { code, message, data } = mapper(e, request);
163 return ErrorResponse(id, code, message, data)
164 };
165
166 const defaultErrorMapper = (stack = '', code = -1) => (e) => {
167 let message = toString('', () => (e ).message);
168 let type = toString(ERROR, (ctor = (e ).constructor) => isFunction(ctor) && ctor.name);
169 const E = globalDOMException();
170 if (E && e instanceof E) type = DOMExceptionHeader + e.name;
171 if (isString(e) || typeof e === 'number' || isBoolean(e) || typeof e === 'bigint') {
172 type = ERROR;
173 message = String(e);
174 }
175 const data = stack ? { stack, type } : { type };
176 return { code, message, data }
177 };
178
179 /**
180 * A JSONRPC response object
181 */
182
183
184 const isJSONRPCObject = (data) => {
185 if (!isObject(data)) return false
186 if (!hasKey(data, 'jsonrpc')) return false
187 if (data.jsonrpc !== jsonrpc) return false
188 if (hasKey(data, 'params')) {
189 const params = (data ).params;
190 if (!isArray(params) && !isObject(params)) return false
191 }
192 return true
193 };
194
195 const hasKey = (
196 obj,
197 key,
198
199
200
201 ) => key in obj;
202
203 const toString = (_default, val) => {
204 try {
205 const v = val();
206 if (v === undefined$1) return _default
207 return String(v)
208 } catch (e2) {
209 return _default
210 }
211 };
212 const deleteUndefined = (x, key) => {
213 if (x[key] === undefined$1) delete x[key];
214 };
215 const deleteFalsy = (x, key) => {
216 if (!x[key]) delete x[key];
217 };
218
219 //#region Serialization
220
221
222
223 /**
224 * Serialization implementation that do nothing
225 * @remarks {@link Serialization}
226 * @public
227 */
228 const NoSerialization = {
229 serialization(from) {
230 return from
231 },
232 deserialization(serialized) {
233 return serialized
234 },
235 };
236
237 /**
238 * Create a serialization by JSON.parse/stringify
239 *
240 * @param replacerAndReceiver - Replacer and receiver of JSON.parse/stringify
241 * @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.
242 * @param undefinedKeepingBehavior - How to keep "undefined" in result of SuccessResponse?
243 *
244 * If it is not handled properly, JSON.stringify will emit an invalid JSON RPC object.
245 *
246 * Options:
247 * - `"null"`(**default**): convert it to null.
248 * - `"keep"`: try to keep it by additional property "undef".
249 * - `false`: Don't keep it, let it break.
250 * @remarks {@link Serialization}
251 * @public
252 */
253 const JSONSerialization = (
254 replacerAndReceiver = [
255 undefined$1,
256 undefined$1,
257 ],
258 space,
259 undefinedKeepingBehavior = 'null',
260 ) => ({
261 serialization(from) {
262 if (undefinedKeepingBehavior && isObject(from) && hasKey(from, 'result') && from.result === undefined$1) {
263 const alt = { ...from };
264 alt.result = null;
265 if (undefinedKeepingBehavior === 'keep') (alt ).undef = true;
266 from = alt;
267 }
268 return JSON.stringify(from, replacerAndReceiver[0], space)
269 },
270 deserialization(serialized) {
271 const result = JSON.parse(serialized , replacerAndReceiver[1]);
272 if (
273 isObject(result) &&
274 hasKey(result, 'result') &&
275 result.result === null &&
276 hasKey(result, 'undef') &&
277 result.undef === true
278 ) {
279 result.result = undefined$1;
280 delete result.undef;
281 }
282 return result
283 },
284 });
285 //#endregion
286
287 const i = 'AsyncCall/';
288 // ! side effect
289 const AsyncCallIgnoreResponse = Symbol.for(i + 'ignored');
290 const AsyncCallNotify = Symbol.for(i + 'notify');
291 const AsyncCallBatch = Symbol.for(i + 'batch');
292
293 /**
294 * Make the returning type to `Promise<void>`
295 * @internal
296 * @remarks
297 * Due to the limitation of TypeScript, generic signatures cannot be preserved
298 * if the function is the top level parameter of this utility type,
299 * or the function is not returning `Promise<void>`.
300 */
301
302
303
304
305
306
307
308
309
310
311
312 /**
313 * Wrap the AsyncCall instance to send notification.
314 * @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance
315 * @example
316 * const notifyOnly = notify(AsyncCall(...))
317 * @public
318 */
319
320 function notify(instanceOrFnOnInstance) {
321 if (isFunction(instanceOrFnOnInstance)) return (instanceOrFnOnInstance )[AsyncCallNotify]
322 return new Proxy(instanceOrFnOnInstance, { get: notifyTrap })
323 }
324 const notifyTrap = (target, p) => {
325 return target[p][AsyncCallNotify]
326 };
327
328 /**
329 * Wrap the AsyncCall instance to use batch call.
330 * @param asyncCallInstance - The AsyncCall instance
331 * @example
332 * const [batched, send, drop] = batch(AsyncCall(...))
333 * batched.call1() // pending
334 * batched.call2() // pending
335 * send() // send all pending requests
336 * drop() // drop all pending requests
337 * @returns It will return a tuple.
338 *
339 * The first item is the batched version of AsyncCall instance passed in.
340 *
341 * The second item is a function to send all pending requests.
342 *
343 * The third item is a function to drop and reject all pending requests.
344 * @public
345 */
346 function batch(asyncCallInstance) {
347 let queue = [];
348 return [
349 new Proxy({ __proto__: null } , {
350 get(cache, p) {
351 if (isString(p) && cache[p]) return cache[p]
352 // @ts-ignore
353 const f = (...args) => asyncCallInstance[AsyncCallBatch](queue, p, ...args);
354 // @ts-ignore
355 f[AsyncCallNotify] = (...args) =>
356 // @ts-ignore
357 asyncCallInstance[AsyncCallBatch][AsyncCallNotify](queue, p, ...args);
358 // @ts-ignore
359 f[AsyncCallNotify][AsyncCallNotify] = f[AsyncCallNotify];
360 isString(p) && Object.defineProperty(cache, p, { value: f, configurable: true });
361 return f
362 },
363 }),
364 (r = queue.r) => r && r[0](),
365 (error = new Error('Aborted'), r = queue.r) => {
366 r && r[1](error);
367 queue = [];
368 },
369 ]
370 }
371
372 const generateRandomID = () => Math.random().toString(36).slice(2);
373
374 const undefinedToTrue = (x) => (x === void 0 ? true : x);
375
376
377
378
379
380
381
382
383
384 const normalizeLogOptions = (log) => {
385 if (log === 'all') return [true, true, true, true, true, true]
386 if (!isBoolean(log)) {
387 const { beCalled, localError, remoteError, type, requestReplay, sendLocalStack } = log;
388 return [
389 undefinedToTrue(beCalled),
390 undefinedToTrue(localError),
391 undefinedToTrue(remoteError),
392 type !== 'basic',
393 requestReplay,
394 sendLocalStack,
395 ]
396 }
397 if (log) return [true, true, true, true]
398 return []
399 };
400
401 const normalizeStrictOptions = (strict) => {
402 if (!isBoolean(strict)) {
403 const { methodNotFound, unknownMessage } = strict;
404 return [methodNotFound, unknownMessage]
405 }
406 return [strict, strict]
407 };
408
409 /**
410 * Create a RPC server & client.
411 *
412 * @remarks
413 * See {@link AsyncCallOptions}
414 *
415 * thisSideImplementation can be a Promise so you can write:
416 *
417 * ```ts
418 * export const service = AsyncCall(typeof window === 'object' ? {} : import('./backend/service.js'), {})
419 * ```
420 *
421 * @param thisSideImplementation - The implementation when this AsyncCall acts as a JSON RPC server. Can be a Promise.
422 * @param options - {@link AsyncCallOptions}
423 * @typeParam OtherSideImplementedFunctions - The type of the API that server expose. For any function on this interface, it will be converted to the async version.
424 * @returns Same as the `OtherSideImplementedFunctions` type parameter, but every function in that interface becomes async and non-function value is removed. Method called "then" are also removed.
425 * @public
426 */
427 function AsyncCall(
428 thisSideImplementation,
429 options,
430 ) {
431 let isThisSideImplementationPending = true;
432 let resolvedThisSideImplementationValue = undefined$1;
433 let rejectedThisSideImplementation = undefined$1;
434 // This promise should never fail
435 const awaitThisSideImplementation = async () => {
436 try {
437 resolvedThisSideImplementationValue = await thisSideImplementation;
438 } catch (e) {
439 rejectedThisSideImplementation = e;
440 console_error('AsyncCall failed to start', e);
441 } finally {
442 isThisSideImplementationPending = false;
443 }
444 };
445
446 const {
447 serializer = NoSerialization,
448 key: logKey = 'rpc',
449 strict = true,
450 log = true,
451 parameterStructures = 'by-position',
452 preferLocalImplementation = false,
453 idGenerator = generateRandomID,
454 mapError,
455 logger,
456 channel,
457 thenable,
458 } = options;
459
460 if (thisSideImplementation instanceof Promise) awaitThisSideImplementation();
461 else {
462 resolvedThisSideImplementationValue = thisSideImplementation;
463 isThisSideImplementationPending = false;
464 }
465
466 const [banMethodNotFound, banUnknownMessage] = normalizeStrictOptions(strict);
467 const [
468 log_beCalled,
469 log_localError,
470 log_remoteError,
471 log_pretty,
472 log_requestReplay,
473 log_sendLocalStack,
474 ] = normalizeLogOptions(log);
475 const {
476 log: console_log,
477 error: console_error = console_log,
478 debug: console_debug = console_log,
479 groupCollapsed: console_groupCollapsed = console_log,
480 groupEnd: console_groupEnd = console_log,
481 warn: console_warn = console_log,
482 } = (logger || console);
483
484 const requestContext = new Map();
485 const onRequest = async (data) => {
486 if (isThisSideImplementationPending) await awaitThisSideImplementation();
487 else {
488 // not pending
489 if (rejectedThisSideImplementation) return makeErrorObject(rejectedThisSideImplementation, '', data)
490 }
491 let frameworkStack = '';
492 try {
493 const { params, method, id: req_id, remoteStack } = data;
494 // ? We're mapping any method starts with 'rpc.' to a Symbol.for
495 const key = (method.startsWith('rpc.') ? Symbol.for(method) : method);
496 const executor =
497 resolvedThisSideImplementationValue && (resolvedThisSideImplementationValue )[key];
498 if (!isFunction(executor)) {
499 if (!banMethodNotFound) {
500 if (log_localError) console_debug('Missing method', key, data);
501 return
502 } else return ErrorResponseMethodNotFound(req_id)
503 }
504 const args = isArray(params) ? params : [params];
505 frameworkStack = removeStackHeader(new Error().stack);
506 const promise = new Promise((resolve) => resolve(executor.apply(resolvedThisSideImplementationValue, args)));
507 if (log_beCalled) {
508 if (log_pretty) {
509 const logArgs = [
510 `${logKey}.%c${method}%c(${args.map(() => '%o').join(', ')}%c)\n%o %c@${req_id}`,
511 'color: #d2c057',
512 '',
513 ...args,
514 '',
515 promise,
516 'color: gray; font-style: italic;',
517 ];
518 if (log_requestReplay) {
519 // This function will be logged to the console so it must be 1 line
520 // prettier-ignore
521 const replay = () => { debugger; return executor.apply(resolvedThisSideImplementationValue, args) };
522 replay.toString = replayFunction;
523 logArgs.push(replay);
524 }
525 if (remoteStack) {
526 console_groupCollapsed(...logArgs);
527 console_log(remoteStack);
528 console_groupEnd();
529 } else console_log(...logArgs);
530 } else console_log(`${logKey}.${method}(${[...args].toString()}) @${req_id}`);
531 }
532 const result = await promise;
533 if (result === AsyncCallIgnoreResponse) return
534 return SuccessResponse(req_id, await promise)
535 } catch (e) {
536 return makeErrorObject(e, frameworkStack, data)
537 }
538 };
539 const onResponse = async (data) => {
540 let errorMessage = '',
541 remoteErrorStack = '',
542 errorCode = 0,
543 errorType = ERROR;
544 if (hasKey(data, 'error')) {
545 const e = data.error;
546 errorMessage = e.message;
547 errorCode = e.code;
548 const detail = e.data;
549
550 if (isObject(detail) && hasKey(detail, 'stack') && isString(detail.stack)) remoteErrorStack = detail.stack;
551 else remoteErrorStack = '<remote stack not available>';
552
553 if (isObject(detail) && hasKey(detail, 'type') && isString(detail.type)) errorType = detail.type;
554 else errorType = ERROR;
555
556 if (log_remoteError)
557 log_pretty
558 ? console_error(
559 `${errorType}: ${errorMessage}(${errorCode}) %c@${data.id}\n%c${remoteErrorStack}`,
560 'color: gray',
561 '',
562 )
563 : console_error(`${errorType}: ${errorMessage}(${errorCode}) @${data.id}\n${remoteErrorStack}`);
564 }
565 if (data.id === null || data.id === undefined$1) return
566 const { f: [resolve, reject] = [null, null], stack: localErrorStack = '' } = requestContext.get(data.id) || {};
567 if (!resolve || !reject) return // drop this response
568 requestContext.delete(data.id);
569 if (hasKey(data, 'error')) {
570 reject(
571 RecoverError(
572 errorType,
573 errorMessage,
574 errorCode,
575 // ? We use \u0430 which looks like "a" to prevent browser think "at AsyncCall" is a real stack
576 remoteErrorStack + '\n \u0430t AsyncCall (rpc) \n' + localErrorStack,
577 ),
578 );
579 } else {
580 resolve(data.result);
581 }
582 return
583 };
584 const rawMessageReceiver = async (_) => {
585 let data;
586 let result = undefined$1;
587 try {
588 data = await deserialization(_);
589 if (isJSONRPCObject(data)) {
590 return (result = await handleSingleMessage(data))
591 } else if (isArray(data) && data.every(isJSONRPCObject) && data.length !== 0) {
592 return Promise.all(data.map(handleSingleMessage))
593 } else {
594 if (banUnknownMessage) {
595 let id = (data ).id;
596 if (id === undefined$1) id = null;
597 return ErrorResponseInvalidRequest(id)
598 } else {
599 // ? Ignore this message. The message channel maybe also used to transfer other message too.
600 return undefined$1
601 }
602 }
603 } catch (e) {
604 if (log_localError) console_error(e, data, result);
605 return ErrorResponseParseError(e, mapError || defaultErrorMapper(e && e.stack))
606 }
607 };
608 const rawMessageSender = async (res) => {
609 if (!res) return
610 if (isArray(res)) {
611 const reply = res.filter((x) => x && hasKey(x, 'id'));
612 if (reply.length === 0) return
613 return serialization(reply)
614 } else {
615 return serialization(res)
616 }
617 };
618 const serialization = (x) => serializer.serialization(x);
619 const deserialization = (x) => serializer.deserialization(x);
620 const isEventBasedChannel = (x) => hasKey(x, 'send') && isFunction(x.send);
621 const isCallbackBasedChannel = (x) =>
622 hasKey(x, 'setup') && isFunction(x.setup);
623
624 if (isCallbackBasedChannel(channel)) {
625 channel.setup(
626 (data) => rawMessageReceiver(data).then(rawMessageSender),
627 (data) => {
628 const _ = deserialization(data);
629 if (isJSONRPCObject(_)) return true
630 return Promise_resolve(_).then(isJSONRPCObject)
631 },
632 );
633 }
634 if (isEventBasedChannel(channel)) {
635 const m = channel;
636 m.on &&
637 m.on((_) =>
638 rawMessageReceiver(_)
639 .then(rawMessageSender)
640 .then((x) => x && m.send(x)),
641 );
642 }
643 function makeErrorObject(e, frameworkStack, data) {
644 if (isObject(e) && hasKey(e, 'stack'))
645 e.stack = frameworkStack
646 .split('\n')
647 .reduce((stack, fstack) => stack.replace(fstack + '\n', ''), '' + e.stack);
648 if (log_localError) console_error(e);
649 return ErrorResponseMapped(data, e, mapError || defaultErrorMapper(log_sendLocalStack ? e.stack : undefined$1))
650 }
651
652 async function sendPayload(payload, removeQueueR = false) {
653 if (removeQueueR) payload = [...(payload )];
654 const data = await serialization(payload);
655 return channel.send(data)
656 }
657 function rejectsQueue(queue, error) {
658 for (const x of queue) {
659 if (hasKey(x, 'id')) {
660 const ctx = requestContext.get(x.id);
661 ctx && ctx.f[1](error);
662 }
663 }
664 }
665 const handleSingleMessage = async (
666 data,
667 ) => {
668 if (hasKey(data, 'method')) {
669 const r = onRequest(data);
670 if (hasKey(data, 'id')) return r
671 try {
672 await r;
673 } catch (e2) {}
674 return undefined$1 // Does not care about return result for notifications
675 }
676 return onResponse(data)
677 };
678 return new Proxy({ __proto__: null } , {
679 get(cache, method) {
680 if (method === 'then') {
681 if (thenable === undefined$1) {
682 console_warn(
683 makeHostedMessage(
684 Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options,
685 new TypeError('RPC used as Promise: '),
686 ),
687 );
688 }
689 if (thenable !== true) return undefined$1
690 }
691 if (isString(method) && cache[method]) return cache[method]
692 const factory = (notify) => (...params) => {
693 let stack = removeStackHeader(new Error().stack);
694 let queue = undefined$1;
695 if (method === AsyncCallBatch) {
696 queue = params.shift();
697 method = params.shift();
698 }
699 if (typeof method === 'symbol') {
700 const RPCInternalMethod = Symbol.keyFor(method) || (method ).description;
701 if (RPCInternalMethod) {
702 if (RPCInternalMethod.startsWith('rpc.')) method = RPCInternalMethod;
703 else return Promise_reject(new TypeError('Not start with rpc.'))
704 }
705 } else if (method.startsWith('rpc.'))
706 return Promise_reject(
707 makeHostedMessage(Err_Cannot_call_method_starts_with_rpc_dot_directly, new TypeError()),
708 )
709 return new Promise((resolve, reject) => {
710 if (preferLocalImplementation && !isThisSideImplementationPending && isString(method)) {
711 const localImpl =
712 resolvedThisSideImplementationValue && (resolvedThisSideImplementationValue )[method];
713 if (isFunction(localImpl)) return resolve(localImpl(...params))
714 }
715 const id = idGenerator();
716 const [param0] = params;
717 const sendingStack = log_sendLocalStack ? stack : '';
718 const param =
719 parameterStructures === 'by-name' && params.length === 1 && isObject(param0) ? param0 : params;
720 const request = Request(notify ? undefined$1 : id, method , param, sendingStack);
721 if (queue) {
722 queue.push(request);
723 if (!queue.r) queue.r = [() => sendPayload(queue, true), (e) => rejectsQueue(queue, e)];
724 } else sendPayload(request).catch(reject);
725 if (notify) return resolve()
726 requestContext.set(id, {
727 f: [resolve, reject],
728 stack,
729 });
730 })
731 };
732 const f = factory(false);
733 // @ts-ignore
734 f[AsyncCallNotify] = factory(true);
735 // @ts-ignore
736 f[AsyncCallNotify][AsyncCallNotify] = f[AsyncCallNotify];
737 isString(method) && Object.defineProperty(cache, method, { value: f, configurable: true });
738 return f
739 },
740 })
741 }
742 // Assume a console object in global if there is no custom logger provided
743
744 exports.AsyncCall = AsyncCall;
745 exports.JSONSerialization = JSONSerialization;
746 exports.NoSerialization = NoSerialization;
747 exports.batch = batch;
748 exports.notify = notify;
749
750 Object.defineProperty(exports, '__esModule', { value: true });
751
752})));
753//# sourceMappingURL=base.js.map