async-call-rpc
Advanced tools
Comparing version 5.0.0 to 5.1.0
/** | ||
* ! This file MUST NOT contain any import statement. | ||
* ! This file is part of public API of this package (for Deno users). | ||
*/ | ||
/** | ||
* This interface represents a "on message" - "send response" model. | ||
@@ -37,103 +41,3 @@ * @remarks | ||
} | ||
/** | ||
* Serialize and deserialize of the JSON RPC payload | ||
* @public | ||
*/ | ||
interface Serialization { | ||
/** | ||
* Serialize data | ||
* @param from - original data | ||
*/ | ||
serialization(from: any): unknown | PromiseLike<unknown>; | ||
/** | ||
* Deserialize data | ||
* @param serialized - Serialized data | ||
*/ | ||
deserialization(serialized: unknown): unknown | PromiseLike<unknown>; | ||
} | ||
/** | ||
* Serialization implementation that do nothing | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const NoSerialization: Serialization; | ||
/** | ||
* Create a serialization by JSON.parse/stringify | ||
* | ||
* @param replacerAndReceiver - Replacer and receiver of JSON.parse/stringify | ||
* @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. | ||
* @param undefinedKeepingBehavior - How to keep "undefined" in result of SuccessResponse? | ||
* | ||
* If it is not handled properly, JSON.stringify will emit an invalid JSON RPC object. | ||
* | ||
* Options: | ||
* - `"null"`(**default**): convert it to null. | ||
* - `"keep"`: try to keep it by additional property "undef". | ||
* - `false`: Don't keep it, let it break. | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const JSONSerialization: (replacerAndReceiver?: [(((key: string, value: any) => any) | undefined)?, (((key: string, value: any) => any) | undefined)?], space?: string | number | undefined, undefinedKeepingBehavior?: 'keep' | 'null' | false) => Serialization; | ||
/** | ||
* The minimal Console interface that AsyncCall needs. | ||
* @public | ||
* @remarks | ||
* The method not provided will use "log" as it's fallback. | ||
*/ | ||
interface Console { | ||
warn?(...args: unknown[]): void; | ||
debug?(...args: unknown[]): void; | ||
log(...args: unknown[]): void; | ||
groupCollapsed?(...args: unknown[]): void; | ||
groupEnd?(...args: unknown[]): void; | ||
error?(...args: unknown[]): void; | ||
} | ||
/** | ||
* Make the returning type to `Promise<void>` | ||
* @internal | ||
* @remarks | ||
* Due to the limitation of TypeScript, generic signatures cannot be preserved | ||
* if the function is the top level parameter of this utility type, | ||
* or the function is not returning `Promise<void>`. | ||
*/ | ||
declare type _IgnoreResponse<T> = T extends (...args: infer Args) => unknown ? (...args: Args) => Promise<void> : { | ||
[key in keyof T as T[key] extends Function ? key : never]: T[key] extends (...args: infer Args) => infer Return ? Return extends Promise<void> ? T[key] : (...args: Args) => Promise<void> : never; | ||
}; | ||
/** | ||
* Wrap the AsyncCall instance to send notification. | ||
* @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance | ||
* @example | ||
* const notifyOnly = notify(AsyncCall(...)) | ||
* @public | ||
*/ | ||
declare function notify<T extends object>(instanceOrFnOnInstance: T): _IgnoreResponse<T>; | ||
/** | ||
* Wrap the AsyncCall instance to use batch call. | ||
* @param asyncCallInstance - The AsyncCall instance | ||
* @example | ||
* const [batched, send, drop] = batch(AsyncCall(...)) | ||
* batched.call1() // pending | ||
* batched.call2() // pending | ||
* send() // send all pending requests | ||
* drop() // drop all pending requests | ||
* @returns It will return a tuple. | ||
* | ||
* The first item is the batched version of AsyncCall instance passed in. | ||
* | ||
* The second item is a function to send all pending requests. | ||
* | ||
* The third item is a function to drop and reject all pending requests. | ||
* @public | ||
*/ | ||
declare function batch<T extends object>(asyncCallInstance: T): [T, () => void, (error?: unknown) => void]; | ||
/** | ||
* See the document at https://github.com/Jack-Works/async-call/ | ||
*/ | ||
/** | ||
* Log options of AsyncCall | ||
@@ -222,3 +126,3 @@ * @remarks | ||
* | ||
* - {@link https://github.com/jack-works/async-call-rpc#web-deno-and-node-bson BSONSerialization} (use the {@link https://npmjs.org/bson | bson} as the serializer) | ||
* - {@link https://github.com/jack-works/async-call-rpc#web-deno-and-node-bson | BSONSerialization} (use the {@link https://npmjs.org/bson | bson} as the serializer) | ||
* | ||
@@ -231,6 +135,6 @@ * @defaultValue {@link NoSerialization} | ||
* @remarks | ||
* See {@link Console} | ||
* See {@link ConsoleInterface} | ||
* @defaultValue globalThis.console | ||
*/ | ||
logger?: Console; | ||
logger?: ConsoleInterface; | ||
/** | ||
@@ -349,2 +253,98 @@ * The message channel to exchange messages between server and client | ||
/** | ||
* The minimal Console interface that AsyncCall needs. | ||
* @public | ||
* @remarks | ||
* The method not provided will use "log" as it's fallback. | ||
*/ | ||
interface ConsoleInterface { | ||
warn?(...args: unknown[]): void; | ||
debug?(...args: unknown[]): void; | ||
log(...args: unknown[]): void; | ||
groupCollapsed?(...args: unknown[]): void; | ||
groupEnd?(...args: unknown[]): void; | ||
error?(...args: unknown[]): void; | ||
} | ||
/** | ||
* Serialize and deserialize of the JSON RPC payload | ||
* @public | ||
*/ | ||
interface Serialization { | ||
/** | ||
* Serialize data | ||
* @param from - original data | ||
*/ | ||
serialization(from: any): unknown | PromiseLike<unknown>; | ||
/** | ||
* Deserialize data | ||
* @param serialized - Serialized data | ||
*/ | ||
deserialization(serialized: unknown): unknown | PromiseLike<unknown>; | ||
} | ||
/** | ||
* Make the returning type to `Promise<void>` | ||
* @internal | ||
* @remarks | ||
* Due to the limitation of TypeScript, generic signatures cannot be preserved | ||
* if the function is the top level parameter of this utility type, | ||
* or the function is not returning `Promise<void>`. | ||
*/ | ||
declare type _IgnoreResponse<T> = T extends (...args: infer Args) => unknown ? (...args: Args) => Promise<void> : { | ||
[key in keyof T as T[key] extends Function ? key : never]: T[key] extends (...args: infer Args) => infer Return ? Return extends Promise<void> ? T[key] : (...args: Args) => Promise<void> : never; | ||
}; | ||
/** | ||
* Wrap the AsyncCall instance to send notification. | ||
* @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance | ||
* @example | ||
* const notifyOnly = notify(AsyncCall(...)) | ||
* @public | ||
*/ | ||
declare function notify<T extends object>(instanceOrFnOnInstance: T): _IgnoreResponse<T>; | ||
/** | ||
* Serialization implementation that do nothing | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const NoSerialization: Serialization; | ||
/** | ||
* Create a serialization by JSON.parse/stringify | ||
* | ||
* @param replacerAndReceiver - Replacer and receiver of JSON.parse/stringify | ||
* @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. | ||
* @param undefinedKeepingBehavior - How to keep "undefined" in result of SuccessResponse? | ||
* | ||
* If it is not handled properly, JSON.stringify will emit an invalid JSON RPC object. | ||
* | ||
* Options: | ||
* - `"null"`(**default**): convert it to null. | ||
* - `"keep"`: try to keep it by additional property "undef". | ||
* - `false`: Don't keep it, let it break. | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const JSONSerialization: (replacerAndReceiver?: [(((key: string, value: any) => any) | undefined)?, (((key: string, value: any) => any) | undefined)?], space?: string | number | undefined, undefinedKeepingBehavior?: 'keep' | 'null' | false) => Serialization; | ||
/** | ||
* Wrap the AsyncCall instance to use batch call. | ||
* @param asyncCallInstance - The AsyncCall instance | ||
* @example | ||
* const [batched, send, drop] = batch(AsyncCall(...)) | ||
* batched.call1() // pending | ||
* batched.call2() // pending | ||
* send() // send all pending requests | ||
* drop() // drop all pending requests | ||
* @returns It will return a tuple. | ||
* | ||
* The first item is the batched version of AsyncCall instance passed in. | ||
* | ||
* The second item is a function to send all pending requests. | ||
* | ||
* The third item is a function to drop and reject all pending requests. | ||
* @public | ||
*/ | ||
declare function batch<T extends object>(asyncCallInstance: T): [T, () => void, (error?: unknown) => void]; | ||
/** | ||
* Create a RPC server & client. | ||
@@ -369,2 +369,2 @@ * | ||
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, CallbackBasedChannel, Console, ErrorMapFunction, EventBasedChannel, JSONRPCRequest, JSONSerialization, NoSerialization, Serialization, _AsyncVersionOf, _IgnoreResponse, batch, notify }; | ||
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, CallbackBasedChannel, ConsoleInterface as Console, ConsoleInterface, ErrorMapFunction, EventBasedChannel, JSONRPCRequest, JSONSerialization, NoSerialization, Serialization, _AsyncVersionOf, _IgnoreResponse, batch, notify }; |
578
out/base.js
@@ -9,12 +9,21 @@ /// <reference types="./base.d.ts" /> | ||
class CustomError extends Error { | ||
constructor(name, message, code, stack) { | ||
super(message); | ||
this.name = name; | ||
this.code = code; | ||
this.stack = stack; | ||
constructor( name, message, code, stack) { | ||
super(message);this.name = name;this.code = code;this.stack = stack; | ||
} | ||
} | ||
const Err_Cannot_find_a_running_iterator_with_given_ID = {}; | ||
const Err_Only_string_can_be_the_RPC_method_name = {}; | ||
const Err_Cannot_call_method_starts_with_rpc_dot_directly = {}; | ||
const Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options = {}; | ||
const Messages = [ | ||
Err_Cannot_find_a_running_iterator_with_given_ID, | ||
Err_Only_string_can_be_the_RPC_method_name, | ||
Err_Cannot_call_method_starts_with_rpc_dot_directly, | ||
Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options, | ||
]; | ||
// https://github.com/Jack-Works/async-call-rpc/wiki/Error-messages | ||
function makeHostedMessage(id, error) { | ||
error.message += `Error ${id}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#` + id; | ||
return error; | ||
const n = Messages.indexOf(id); | ||
error.message += `Error ${n}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#` + n; | ||
return error | ||
} | ||
@@ -43,5 +52,4 @@ // ! side effect | ||
const name = type.slice(DOMExceptionHeader.length); | ||
return new E(message, name); | ||
} | ||
else if (type in errors) { | ||
return new E(message, name) | ||
} else if (type in errors) { | ||
const e = new errors[type](message); | ||
@@ -51,11 +59,9 @@ e.stack = stack; | ||
e.code = code; | ||
return e; | ||
return e | ||
} else { | ||
return new CustomError(type, message, code, stack) | ||
} | ||
else { | ||
return new CustomError(type, message, code, stack); | ||
} | ||
} catch (e2) { | ||
return new Error(`E${code} ${type}: ${message}\n${stack}`) | ||
} | ||
catch (_a) { | ||
return new Error(`E${code} ${type}: ${message}\n${stack}`); | ||
} | ||
}; | ||
@@ -67,5 +73,4 @@ const removeStackHeader = (stack) => String(stack).replace(/^.+\n.+\n/, ''); | ||
// @ts-ignore | ||
return DOMException; | ||
} | ||
catch (_a) { } | ||
return DOMException | ||
} catch (e3) {} | ||
}); | ||
@@ -82,4 +87,18 @@ | ||
const isArray = Array.isArray; | ||
const replayFunction = () => '() => replay()'; | ||
const jsonrpc = '2.0'; | ||
const Request = (id, method, params, remoteStack) => { | ||
@@ -89,18 +108,38 @@ const x = { jsonrpc, id, method, params, remoteStack }; | ||
deleteFalsy(x, 'remoteStack'); | ||
return x; | ||
return x | ||
}; | ||
/** | ||
* JSONRPC SuccessResponse object. | ||
*/ | ||
const SuccessResponse = (id, result) => { | ||
const x = { jsonrpc, id, result }; | ||
deleteUndefined(x, 'id'); | ||
return x; | ||
return x | ||
}; | ||
/** | ||
* JSONRPC ErrorResponse object. | ||
* @public | ||
*/ | ||
const ErrorResponse = (id, code, message, data) => { | ||
if (id === undefined$1) | ||
id = null; | ||
if (id === undefined$1) id = null; | ||
code = Math.floor(code); | ||
if (Number.isNaN(code)) | ||
code = -1; | ||
if (Number.isNaN(code)) code = -1; | ||
const x = { jsonrpc, id, error: { code, message, data } }; | ||
deleteUndefined(x.error, 'data'); | ||
return x; | ||
return x | ||
}; | ||
@@ -110,8 +149,9 @@ // Pre defined error in section 5.1 | ||
const ErrorResponseParseError = (e, mapper) => { | ||
const obj = ErrorResponseMapped({}, e, mapper); | ||
const o = obj.error; | ||
const obj = ErrorResponseMapped({} , e, mapper); | ||
const o = obj.error; | ||
o.code = -32700; | ||
o.message = 'Parse error'; | ||
return obj; | ||
return obj | ||
}; | ||
// Not using. | ||
@@ -122,13 +162,18 @@ // InvalidParams -32602 'Invalid params' | ||
const ErrorResponseMethodNotFound = (id) => ErrorResponse(id, -32601, 'Method not found'); | ||
const ErrorResponseMapped = (request, e, mapper) => { | ||
const { id } = request; | ||
const { code, message, data } = mapper(e, request); | ||
return ErrorResponse(id, code, message, data); | ||
return ErrorResponse(id, code, message, data) | ||
}; | ||
const defaultErrorMapper = (stack = '', code = -1) => (e) => { | ||
let message = toString('', () => e.message); | ||
let type = toString(ERROR, (ctor = e.constructor) => isFunction(ctor) && ctor.name); | ||
let message = toString('', () => (e ).message); | ||
let type = toString(ERROR, (ctor = (e ).constructor) => isFunction(ctor) && ctor.name); | ||
const E = globalDOMException(); | ||
if (E && e instanceof E) | ||
type = DOMExceptionHeader + e.name; | ||
if (E && e instanceof E) type = DOMExceptionHeader + e.name; | ||
if (isString(e) || typeof e === 'number' || isBoolean(e) || typeof e === 'bigint') { | ||
@@ -139,40 +184,49 @@ type = ERROR; | ||
const data = stack ? { stack, type } : { type }; | ||
return { code, message, data }; | ||
return { code, message, data } | ||
}; | ||
/** | ||
* A JSONRPC response object | ||
*/ | ||
const isJSONRPCObject = (data) => { | ||
if (!isObject(data)) | ||
return false; | ||
if (!hasKey(data, 'jsonrpc')) | ||
return false; | ||
if (data.jsonrpc !== jsonrpc) | ||
return false; | ||
if (!isObject(data)) return false | ||
if (!hasKey(data, 'jsonrpc')) return false | ||
if (data.jsonrpc !== jsonrpc) return false | ||
if (hasKey(data, 'params')) { | ||
const params = data.params; | ||
if (!isArray(params) && !isObject(params)) | ||
return false; | ||
const params = (data ).params; | ||
if (!isArray(params) && !isObject(params)) return false | ||
} | ||
return true; | ||
return true | ||
}; | ||
const hasKey = (obj, key) => key in obj; | ||
const hasKey = ( | ||
obj, | ||
key, | ||
) => key in obj; | ||
const toString = (_default, val) => { | ||
try { | ||
const v = val(); | ||
if (v === undefined$1) | ||
return _default; | ||
return String(v); | ||
if (v === undefined$1) return _default | ||
return String(v) | ||
} catch (e2) { | ||
return _default | ||
} | ||
catch (_a) { | ||
return _default; | ||
} | ||
}; | ||
const deleteUndefined = (x, key) => { | ||
if (x[key] === undefined$1) | ||
delete x[key]; | ||
if (x[key] === undefined$1) delete x[key]; | ||
}; | ||
const deleteFalsy = (x, key) => { | ||
if (!x[key]) | ||
delete x[key]; | ||
if (!x[key]) delete x[key]; | ||
}; | ||
//#region Serialization | ||
/** | ||
@@ -185,8 +239,9 @@ * Serialization implementation that do nothing | ||
serialization(from) { | ||
return from; | ||
return from | ||
}, | ||
deserialization(serialized) { | ||
return serialized; | ||
return serialized | ||
}, | ||
}; | ||
/** | ||
@@ -208,6 +263,10 @@ * Create a serialization by JSON.parse/stringify | ||
*/ | ||
const JSONSerialization = (replacerAndReceiver = [ | ||
undefined$1, | ||
undefined$1, | ||
], space, undefinedKeepingBehavior = 'null') => ({ | ||
const JSONSerialization = ( | ||
replacerAndReceiver = [ | ||
undefined$1, | ||
undefined$1, | ||
], | ||
space, | ||
undefinedKeepingBehavior = 'null', | ||
) => ({ | ||
serialization(from) { | ||
@@ -217,19 +276,20 @@ if (undefinedKeepingBehavior && isObject(from) && hasKey(from, 'result') && from.result === undefined$1) { | ||
alt.result = null; | ||
if (undefinedKeepingBehavior === 'keep') | ||
alt.undef = true; | ||
if (undefinedKeepingBehavior === 'keep') (alt ).undef = true; | ||
from = alt; | ||
} | ||
return JSON.stringify(from, replacerAndReceiver[0], space); | ||
return JSON.stringify(from, replacerAndReceiver[0], space) | ||
}, | ||
deserialization(serialized) { | ||
const result = JSON.parse(serialized, replacerAndReceiver[1]); | ||
if (isObject(result) && | ||
const result = JSON.parse(serialized , replacerAndReceiver[1]); | ||
if ( | ||
isObject(result) && | ||
hasKey(result, 'result') && | ||
result.result === null && | ||
hasKey(result, 'undef') && | ||
result.undef === true) { | ||
result.undef === true | ||
) { | ||
result.result = undefined$1; | ||
delete result.undef; | ||
} | ||
return result; | ||
return result | ||
}, | ||
@@ -246,2 +306,21 @@ }); | ||
/** | ||
* Make the returning type to `Promise<void>` | ||
* @internal | ||
* @remarks | ||
* Due to the limitation of TypeScript, generic signatures cannot be preserved | ||
* if the function is the top level parameter of this utility type, | ||
* or the function is not returning `Promise<void>`. | ||
*/ | ||
/** | ||
* Wrap the AsyncCall instance to send notification. | ||
@@ -253,9 +332,9 @@ * @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance | ||
*/ | ||
function notify(instanceOrFnOnInstance) { | ||
if (isFunction(instanceOrFnOnInstance)) | ||
return instanceOrFnOnInstance[AsyncCallNotify]; | ||
return new Proxy(instanceOrFnOnInstance, { get: notifyTrap }); | ||
if (isFunction(instanceOrFnOnInstance)) return (instanceOrFnOnInstance )[AsyncCallNotify] | ||
return new Proxy(instanceOrFnOnInstance, { get: notifyTrap }) | ||
} | ||
const notifyTrap = (target, p) => { | ||
return target[p][AsyncCallNotify]; | ||
return target[p][AsyncCallNotify] | ||
}; | ||
@@ -282,18 +361,17 @@ | ||
function batch(asyncCallInstance) { | ||
let queue = []; | ||
let queue = []; | ||
return [ | ||
new Proxy({ __proto__: null }, { | ||
new Proxy({ __proto__: null } , { | ||
get(cache, p) { | ||
if (isString(p) && cache[p]) | ||
return cache[p]; | ||
if (isString(p) && cache[p]) return cache[p] | ||
// @ts-ignore | ||
const f = (...args) => asyncCallInstance[AsyncCallBatch](queue, p, ...args); | ||
// @ts-ignore | ||
f[AsyncCallNotify] = (...args) => | ||
f[AsyncCallNotify] = (...args) => | ||
// @ts-ignore | ||
asyncCallInstance[AsyncCallBatch][AsyncCallNotify](queue, p, ...args); | ||
// @ts-ignore | ||
asyncCallInstance[AsyncCallBatch][AsyncCallNotify](queue, p, ...args); | ||
// @ts-ignore | ||
f[AsyncCallNotify][AsyncCallNotify] = f[AsyncCallNotify]; | ||
isString(p) && Object.defineProperty(cache, p, { value: f, configurable: true }); | ||
return f; | ||
return f | ||
}, | ||
@@ -306,3 +384,3 @@ }), | ||
}, | ||
]; | ||
] | ||
} | ||
@@ -313,5 +391,13 @@ | ||
const undefinedToTrue = (x) => (x === void 0 ? true : x); | ||
const normalizeLogOptions = (log) => { | ||
if (log === 'all') | ||
return [true, true, true, true, true, true]; | ||
if (log === 'all') return [true, true, true, true, true, true] | ||
if (!isBoolean(log)) { | ||
@@ -326,20 +412,17 @@ const { beCalled, localError, remoteError, type, requestReplay, sendLocalStack } = log; | ||
sendLocalStack, | ||
]; | ||
] | ||
} | ||
if (log) | ||
return [true, true, true, true]; | ||
return []; | ||
if (log) return [true, true, true, true] | ||
return [] | ||
}; | ||
const normalizeStrictOptions = (strict) => { | ||
if (!isBoolean(strict)) { | ||
const { methodNotFound, unknownMessage } = strict; | ||
return [methodNotFound, unknownMessage]; | ||
return [methodNotFound, unknownMessage] | ||
} | ||
return [strict, strict]; | ||
return [strict, strict] | ||
}; | ||
/** | ||
* See the document at https://github.com/Jack-Works/async-call/ | ||
*/ | ||
/** | ||
* Create a RPC server & client. | ||
@@ -362,3 +445,6 @@ * | ||
*/ | ||
function AsyncCall(thisSideImplementation, options) { | ||
function AsyncCall( | ||
thisSideImplementation, | ||
options, | ||
) { | ||
let isThisSideImplementationPending = true; | ||
@@ -371,14 +457,25 @@ let resolvedThisSideImplementationValue = undefined$1; | ||
resolvedThisSideImplementationValue = await thisSideImplementation; | ||
} | ||
catch (e) { | ||
} catch (e) { | ||
rejectedThisSideImplementation = e; | ||
console_error('AsyncCall failed to start', e); | ||
} | ||
finally { | ||
} finally { | ||
isThisSideImplementationPending = false; | ||
} | ||
}; | ||
const { serializer = NoSerialization, key: logKey = 'rpc', strict = true, log = true, parameterStructures = 'by-position', preferLocalImplementation = false, idGenerator = generateRandomID, mapError, logger, channel, thenable, } = options; | ||
if (thisSideImplementation instanceof Promise) | ||
awaitThisSideImplementation(); | ||
const { | ||
serializer = NoSerialization, | ||
key: logKey = 'rpc', | ||
strict = true, | ||
log = true, | ||
parameterStructures = 'by-position', | ||
preferLocalImplementation = false, | ||
idGenerator = generateRandomID, | ||
mapError, | ||
logger, | ||
channel, | ||
thenable, | ||
} = options; | ||
if (thisSideImplementation instanceof Promise) awaitThisSideImplementation(); | ||
else { | ||
@@ -388,13 +485,27 @@ resolvedThisSideImplementationValue = thisSideImplementation; | ||
} | ||
const [banMethodNotFound, banUnknownMessage] = normalizeStrictOptions(strict); | ||
const [log_beCalled, log_localError, log_remoteError, log_pretty, log_requestReplay, log_sendLocalStack,] = normalizeLogOptions(log); | ||
const { log: console_log, error: console_error = console_log, debug: console_debug = console_log, groupCollapsed: console_groupCollapsed = console_log, groupEnd: console_groupEnd = console_log, warn: console_warn = console_log, } = (logger || console); | ||
const [ | ||
log_beCalled, | ||
log_localError, | ||
log_remoteError, | ||
log_pretty, | ||
log_requestReplay, | ||
log_sendLocalStack, | ||
] = normalizeLogOptions(log); | ||
const { | ||
log: console_log, | ||
error: console_error = console_log, | ||
debug: console_debug = console_log, | ||
groupCollapsed: console_groupCollapsed = console_log, | ||
groupEnd: console_groupEnd = console_log, | ||
warn: console_warn = console_log, | ||
} = (logger || console); | ||
const requestContext = new Map(); | ||
const onRequest = async (data) => { | ||
if (isThisSideImplementationPending) | ||
await awaitThisSideImplementation(); | ||
if (isThisSideImplementationPending) await awaitThisSideImplementation(); | ||
else { | ||
// not pending | ||
if (rejectedThisSideImplementation) | ||
return makeErrorObject(rejectedThisSideImplementation, '', data); | ||
if (rejectedThisSideImplementation) return makeErrorObject(rejectedThisSideImplementation, '', data) | ||
} | ||
@@ -405,12 +516,10 @@ let frameworkStack = ''; | ||
// ? We're mapping any method starts with 'rpc.' to a Symbol.for | ||
const key = (method.startsWith('rpc.') ? Symbol.for(method) : method); | ||
const executor = resolvedThisSideImplementationValue && resolvedThisSideImplementationValue[key]; | ||
const key = (method.startsWith('rpc.') ? Symbol.for(method) : method); | ||
const executor = | ||
resolvedThisSideImplementationValue && (resolvedThisSideImplementationValue )[key]; | ||
if (!isFunction(executor)) { | ||
if (!banMethodNotFound) { | ||
if (log_localError) | ||
console_debug('Missing method', key, data); | ||
return; | ||
} | ||
else | ||
return ErrorResponseMethodNotFound(req_id); | ||
if (log_localError) console_debug('Missing method', key, data); | ||
return | ||
} else return ErrorResponseMethodNotFound(req_id) | ||
} | ||
@@ -431,7 +540,9 @@ const args = isArray(params) ? params : [params]; | ||
]; | ||
if (log_requestReplay) | ||
logArgs.push(() => { | ||
debugger; | ||
return executor.apply(resolvedThisSideImplementationValue, args); | ||
}); | ||
if (log_requestReplay) { | ||
// This function will be logged to the console so it must be 1 line | ||
// prettier-ignore | ||
const replay = () => { debugger; return executor.apply(resolvedThisSideImplementationValue, args) }; | ||
replay.toString = replayFunction; | ||
logArgs.push(replay); | ||
} | ||
if (remoteStack) { | ||
@@ -441,20 +552,17 @@ console_groupCollapsed(...logArgs); | ||
console_groupEnd(); | ||
} | ||
else | ||
console_log(...logArgs); | ||
} | ||
else | ||
console_log(`${logKey}.${method}(${[...args].toString()}) @${req_id}`); | ||
} else console_log(...logArgs); | ||
} else console_log(`${logKey}.${method}(${[...args].toString()}) @${req_id}`); | ||
} | ||
const result = await promise; | ||
if (result === AsyncCallIgnoreResponse) | ||
return; | ||
return SuccessResponse(req_id, await promise); | ||
if (result === AsyncCallIgnoreResponse) return | ||
return SuccessResponse(req_id, await promise) | ||
} catch (e) { | ||
return makeErrorObject(e, frameworkStack, data) | ||
} | ||
catch (e) { | ||
return makeErrorObject(e, frameworkStack, data); | ||
} | ||
}; | ||
const onResponse = async (data) => { | ||
let errorMessage = '', remoteErrorStack = '', errorCode = 0, errorType = ERROR; | ||
let errorMessage = '', | ||
remoteErrorStack = '', | ||
errorCode = 0, | ||
errorType = ERROR; | ||
if (hasKey(data, 'error')) { | ||
@@ -465,30 +573,36 @@ const e = data.error; | ||
const detail = e.data; | ||
if (isObject(detail) && hasKey(detail, 'stack') && isString(detail.stack)) | ||
remoteErrorStack = detail.stack; | ||
else | ||
remoteErrorStack = '<remote stack not available>'; | ||
if (isObject(detail) && hasKey(detail, 'type') && isString(detail.type)) | ||
errorType = detail.type; | ||
else | ||
errorType = ERROR; | ||
if (isObject(detail) && hasKey(detail, 'stack') && isString(detail.stack)) remoteErrorStack = detail.stack; | ||
else remoteErrorStack = '<remote stack not available>'; | ||
if (isObject(detail) && hasKey(detail, 'type') && isString(detail.type)) errorType = detail.type; | ||
else errorType = ERROR; | ||
if (log_remoteError) | ||
log_pretty | ||
? console_error(`${errorType}: ${errorMessage}(${errorCode}) %c@${data.id}\n%c${remoteErrorStack}`, 'color: gray', '') | ||
? console_error( | ||
`${errorType}: ${errorMessage}(${errorCode}) %c@${data.id}\n%c${remoteErrorStack}`, | ||
'color: gray', | ||
'', | ||
) | ||
: console_error(`${errorType}: ${errorMessage}(${errorCode}) @${data.id}\n${remoteErrorStack}`); | ||
} | ||
if (data.id === null || data.id === undefined$1) | ||
return; | ||
if (data.id === null || data.id === undefined$1) return | ||
const { f: [resolve, reject] = [null, null], stack: localErrorStack = '' } = requestContext.get(data.id) || {}; | ||
if (!resolve || !reject) | ||
return; // drop this response | ||
if (!resolve || !reject) return // drop this response | ||
requestContext.delete(data.id); | ||
if (hasKey(data, 'error')) { | ||
reject(RecoverError(errorType, errorMessage, errorCode, | ||
// ? We use \u0430 which looks like "a" to prevent browser think "at AsyncCall" is a real stack | ||
remoteErrorStack + '\n \u0430t AsyncCall (rpc) \n' + localErrorStack)); | ||
} | ||
else { | ||
reject( | ||
RecoverError( | ||
errorType, | ||
errorMessage, | ||
errorCode, | ||
// ? We use \u0430 which looks like "a" to prevent browser think "at AsyncCall" is a real stack | ||
remoteErrorStack + '\n \u0430t AsyncCall (rpc) \n' + localErrorStack, | ||
), | ||
); | ||
} else { | ||
resolve(data.result); | ||
} | ||
return; | ||
return | ||
}; | ||
@@ -501,38 +615,29 @@ const rawMessageReceiver = async (_) => { | ||
if (isJSONRPCObject(data)) { | ||
return (result = await handleSingleMessage(data)); | ||
} | ||
else if (isArray(data) && data.every(isJSONRPCObject) && data.length !== 0) { | ||
return Promise.all(data.map(handleSingleMessage)); | ||
} | ||
else { | ||
return (result = await handleSingleMessage(data)) | ||
} else if (isArray(data) && data.every(isJSONRPCObject) && data.length !== 0) { | ||
return Promise.all(data.map(handleSingleMessage)) | ||
} else { | ||
if (banUnknownMessage) { | ||
let id = data.id; | ||
if (id === undefined$1) | ||
id = null; | ||
return ErrorResponseInvalidRequest(id); | ||
} | ||
else { | ||
let id = (data ).id; | ||
if (id === undefined$1) id = null; | ||
return ErrorResponseInvalidRequest(id) | ||
} else { | ||
// ? Ignore this message. The message channel maybe also used to transfer other message too. | ||
return undefined$1; | ||
return undefined$1 | ||
} | ||
} | ||
} catch (e) { | ||
if (log_localError) console_error(e, data, result); | ||
return ErrorResponseParseError(e, mapError || defaultErrorMapper(e && e.stack)) | ||
} | ||
catch (e) { | ||
if (log_localError) | ||
console_error(e, data, result); | ||
return ErrorResponseParseError(e, mapError || defaultErrorMapper(e && e.stack)); | ||
} | ||
}; | ||
const rawMessageSender = async (res) => { | ||
if (!res) | ||
return; | ||
if (!res) return | ||
if (isArray(res)) { | ||
const reply = res.filter((x) => x && hasKey(x, 'id')); | ||
if (reply.length === 0) | ||
return; | ||
return serialization(reply); | ||
if (reply.length === 0) return | ||
return serialization(reply) | ||
} else { | ||
return serialization(res) | ||
} | ||
else { | ||
return serialization(res); | ||
} | ||
}; | ||
@@ -542,17 +647,23 @@ const serialization = (x) => serializer.serialization(x); | ||
const isEventBasedChannel = (x) => hasKey(x, 'send') && isFunction(x.send); | ||
const isCallbackBasedChannel = (x) => hasKey(x, 'setup') && isFunction(x.setup); | ||
const isCallbackBasedChannel = (x) => | ||
hasKey(x, 'setup') && isFunction(x.setup); | ||
if (isCallbackBasedChannel(channel)) { | ||
channel.setup((data) => rawMessageReceiver(data).then(rawMessageSender), (data) => { | ||
const _ = deserialization(data); | ||
if (isJSONRPCObject(_)) | ||
return true; | ||
return Promise_resolve(_).then(isJSONRPCObject); | ||
}); | ||
channel.setup( | ||
(data) => rawMessageReceiver(data).then(rawMessageSender), | ||
(data) => { | ||
const _ = deserialization(data); | ||
if (isJSONRPCObject(_)) return true | ||
return Promise_resolve(_).then(isJSONRPCObject) | ||
}, | ||
); | ||
} | ||
if (isEventBasedChannel(channel)) { | ||
const m = channel; | ||
const m = channel; | ||
m.on && | ||
m.on((_) => rawMessageReceiver(_) | ||
.then(rawMessageSender) | ||
.then((x) => x && m.send(x))); | ||
m.on((_) => | ||
rawMessageReceiver(_) | ||
.then(rawMessageSender) | ||
.then((x) => x && m.send(x)), | ||
); | ||
} | ||
@@ -564,11 +675,10 @@ function makeErrorObject(e, frameworkStack, data) { | ||
.reduce((stack, fstack) => stack.replace(fstack + '\n', ''), '' + e.stack); | ||
if (log_localError) | ||
console_error(e); | ||
return ErrorResponseMapped(data, e, mapError || defaultErrorMapper(log_sendLocalStack ? e.stack : undefined$1)); | ||
if (log_localError) console_error(e); | ||
return ErrorResponseMapped(data, e, mapError || defaultErrorMapper(log_sendLocalStack ? e.stack : undefined$1)) | ||
} | ||
async function sendPayload(payload, removeQueueR = false) { | ||
if (removeQueueR) | ||
payload = [...payload]; | ||
if (removeQueueR) payload = [...(payload )]; | ||
const data = await serialization(payload); | ||
return channel.send(data); | ||
return channel.send(data) | ||
} | ||
@@ -583,26 +693,29 @@ function rejectsQueue(queue, error) { | ||
} | ||
const handleSingleMessage = async (data) => { | ||
const handleSingleMessage = async ( | ||
data, | ||
) => { | ||
if (hasKey(data, 'method')) { | ||
const r = onRequest(data); | ||
if (hasKey(data, 'id')) | ||
return r; | ||
if (hasKey(data, 'id')) return r | ||
try { | ||
await r; | ||
} | ||
catch (_a) { } | ||
return undefined$1; // Does not care about return result for notifications | ||
} catch (e2) {} | ||
return undefined$1 // Does not care about return result for notifications | ||
} | ||
return onResponse(data); | ||
return onResponse(data) | ||
}; | ||
return new Proxy({ __proto__: null }, { | ||
return new Proxy({ __proto__: null } , { | ||
get(cache, method) { | ||
if (method === 'then') { | ||
if (thenable === undefined$1) { | ||
console_warn(makeHostedMessage(3 /* Instance_is_treated_as_Promise_please_explicitly_mark_if_it_is_thenable_or_not_via_the_options */, new TypeError('RPC used as Promise: '))); | ||
console_warn( | ||
makeHostedMessage( | ||
Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options, | ||
new TypeError('RPC used as Promise: '), | ||
), | ||
); | ||
} | ||
if (thenable !== true) | ||
return undefined$1; | ||
if (thenable !== true) return undefined$1 | ||
} | ||
if (isString(method) && cache[method]) | ||
return cache[method]; | ||
if (isString(method) && cache[method]) return cache[method] | ||
const factory = (notify) => (...params) => { | ||
@@ -612,21 +725,20 @@ let stack = removeStackHeader(new Error().stack); | ||
if (method === AsyncCallBatch) { | ||
queue = params.shift(); | ||
method = params.shift(); | ||
queue = params.shift(); | ||
method = params.shift(); | ||
} | ||
if (typeof method === 'symbol') { | ||
const RPCInternalMethod = Symbol.keyFor(method) || method.description; | ||
const RPCInternalMethod = Symbol.keyFor(method) || (method ).description; | ||
if (RPCInternalMethod) { | ||
if (RPCInternalMethod.startsWith('rpc.')) | ||
method = RPCInternalMethod; | ||
else | ||
return Promise_reject(new TypeError('Not start with rpc.')); | ||
if (RPCInternalMethod.startsWith('rpc.')) method = RPCInternalMethod; | ||
else return Promise_reject(new TypeError('Not start with rpc.')) | ||
} | ||
} | ||
else if (method.startsWith('rpc.')) | ||
return Promise_reject(makeHostedMessage(2 /* Can_not_call_method_starts_with_rpc_dot_directly */, new TypeError())); | ||
} else if (method.startsWith('rpc.')) | ||
return Promise_reject( | ||
makeHostedMessage(Err_Cannot_call_method_starts_with_rpc_dot_directly, new TypeError()), | ||
) | ||
return new Promise((resolve, reject) => { | ||
if (preferLocalImplementation && !isThisSideImplementationPending && isString(method)) { | ||
const localImpl = resolvedThisSideImplementationValue && resolvedThisSideImplementationValue[method]; | ||
if (isFunction(localImpl)) | ||
return resolve(localImpl(...params)); | ||
const localImpl = | ||
resolvedThisSideImplementationValue && (resolvedThisSideImplementationValue )[method]; | ||
if (isFunction(localImpl)) return resolve(localImpl(...params)) | ||
} | ||
@@ -636,13 +748,10 @@ const id = idGenerator(); | ||
const sendingStack = log_sendLocalStack ? stack : ''; | ||
const param = parameterStructures === 'by-name' && params.length === 1 && isObject(param0) ? param0 : params; | ||
const request = Request(notify ? undefined$1 : id, method, param, sendingStack); | ||
const param = | ||
parameterStructures === 'by-name' && params.length === 1 && isObject(param0) ? param0 : params; | ||
const request = Request(notify ? undefined$1 : id, method , param, sendingStack); | ||
if (queue) { | ||
queue.push(request); | ||
if (!queue.r) | ||
queue.r = [() => sendPayload(queue, true), (e) => rejectsQueue(queue, e)]; | ||
} | ||
else | ||
sendPayload(request).catch(reject); | ||
if (notify) | ||
return resolve(); | ||
if (!queue.r) queue.r = [() => sendPayload(queue, true), (e) => rejectsQueue(queue, e)]; | ||
} else sendPayload(request).catch(reject); | ||
if (notify) return resolve() | ||
requestContext.set(id, { | ||
@@ -652,3 +761,3 @@ f: [resolve, reject], | ||
}); | ||
}); | ||
}) | ||
}; | ||
@@ -661,6 +770,7 @@ const f = factory(false); | ||
isString(method) && Object.defineProperty(cache, method, { value: f, configurable: true }); | ||
return f; | ||
return f | ||
}, | ||
}); | ||
}) | ||
} | ||
// Assume a console object in global if there is no custom logger provided | ||
@@ -667,0 +777,0 @@ exports.AsyncCall = AsyncCall; |
/** | ||
* ! This file MUST NOT contain any import statement. | ||
* ! This file is part of public API of this package (for Deno users). | ||
*/ | ||
/** | ||
* This interface represents a "on message" - "send response" model. | ||
@@ -37,103 +41,3 @@ * @remarks | ||
} | ||
/** | ||
* Serialize and deserialize of the JSON RPC payload | ||
* @public | ||
*/ | ||
interface Serialization { | ||
/** | ||
* Serialize data | ||
* @param from - original data | ||
*/ | ||
serialization(from: any): unknown | PromiseLike<unknown>; | ||
/** | ||
* Deserialize data | ||
* @param serialized - Serialized data | ||
*/ | ||
deserialization(serialized: unknown): unknown | PromiseLike<unknown>; | ||
} | ||
/** | ||
* Serialization implementation that do nothing | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const NoSerialization: Serialization; | ||
/** | ||
* Create a serialization by JSON.parse/stringify | ||
* | ||
* @param replacerAndReceiver - Replacer and receiver of JSON.parse/stringify | ||
* @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. | ||
* @param undefinedKeepingBehavior - How to keep "undefined" in result of SuccessResponse? | ||
* | ||
* If it is not handled properly, JSON.stringify will emit an invalid JSON RPC object. | ||
* | ||
* Options: | ||
* - `"null"`(**default**): convert it to null. | ||
* - `"keep"`: try to keep it by additional property "undef". | ||
* - `false`: Don't keep it, let it break. | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const JSONSerialization: (replacerAndReceiver?: [(((key: string, value: any) => any) | undefined)?, (((key: string, value: any) => any) | undefined)?], space?: string | number | undefined, undefinedKeepingBehavior?: 'keep' | 'null' | false) => Serialization; | ||
/** | ||
* The minimal Console interface that AsyncCall needs. | ||
* @public | ||
* @remarks | ||
* The method not provided will use "log" as it's fallback. | ||
*/ | ||
interface Console { | ||
warn?(...args: unknown[]): void; | ||
debug?(...args: unknown[]): void; | ||
log(...args: unknown[]): void; | ||
groupCollapsed?(...args: unknown[]): void; | ||
groupEnd?(...args: unknown[]): void; | ||
error?(...args: unknown[]): void; | ||
} | ||
/** | ||
* Make the returning type to `Promise<void>` | ||
* @internal | ||
* @remarks | ||
* Due to the limitation of TypeScript, generic signatures cannot be preserved | ||
* if the function is the top level parameter of this utility type, | ||
* or the function is not returning `Promise<void>`. | ||
*/ | ||
declare type _IgnoreResponse<T> = T extends (...args: infer Args) => unknown ? (...args: Args) => Promise<void> : { | ||
[key in keyof T as T[key] extends Function ? key : never]: T[key] extends (...args: infer Args) => infer Return ? Return extends Promise<void> ? T[key] : (...args: Args) => Promise<void> : never; | ||
}; | ||
/** | ||
* Wrap the AsyncCall instance to send notification. | ||
* @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance | ||
* @example | ||
* const notifyOnly = notify(AsyncCall(...)) | ||
* @public | ||
*/ | ||
declare function notify<T extends object>(instanceOrFnOnInstance: T): _IgnoreResponse<T>; | ||
/** | ||
* Wrap the AsyncCall instance to use batch call. | ||
* @param asyncCallInstance - The AsyncCall instance | ||
* @example | ||
* const [batched, send, drop] = batch(AsyncCall(...)) | ||
* batched.call1() // pending | ||
* batched.call2() // pending | ||
* send() // send all pending requests | ||
* drop() // drop all pending requests | ||
* @returns It will return a tuple. | ||
* | ||
* The first item is the batched version of AsyncCall instance passed in. | ||
* | ||
* The second item is a function to send all pending requests. | ||
* | ||
* The third item is a function to drop and reject all pending requests. | ||
* @public | ||
*/ | ||
declare function batch<T extends object>(asyncCallInstance: T): [T, () => void, (error?: unknown) => void]; | ||
/** | ||
* See the document at https://github.com/Jack-Works/async-call/ | ||
*/ | ||
/** | ||
* Log options of AsyncCall | ||
@@ -222,3 +126,3 @@ * @remarks | ||
* | ||
* - {@link https://github.com/jack-works/async-call-rpc#web-deno-and-node-bson BSONSerialization} (use the {@link https://npmjs.org/bson | bson} as the serializer) | ||
* - {@link https://github.com/jack-works/async-call-rpc#web-deno-and-node-bson | BSONSerialization} (use the {@link https://npmjs.org/bson | bson} as the serializer) | ||
* | ||
@@ -231,6 +135,6 @@ * @defaultValue {@link NoSerialization} | ||
* @remarks | ||
* See {@link Console} | ||
* See {@link ConsoleInterface} | ||
* @defaultValue globalThis.console | ||
*/ | ||
logger?: Console; | ||
logger?: ConsoleInterface; | ||
/** | ||
@@ -349,2 +253,98 @@ * The message channel to exchange messages between server and client | ||
/** | ||
* The minimal Console interface that AsyncCall needs. | ||
* @public | ||
* @remarks | ||
* The method not provided will use "log" as it's fallback. | ||
*/ | ||
interface ConsoleInterface { | ||
warn?(...args: unknown[]): void; | ||
debug?(...args: unknown[]): void; | ||
log(...args: unknown[]): void; | ||
groupCollapsed?(...args: unknown[]): void; | ||
groupEnd?(...args: unknown[]): void; | ||
error?(...args: unknown[]): void; | ||
} | ||
/** | ||
* Serialize and deserialize of the JSON RPC payload | ||
* @public | ||
*/ | ||
interface Serialization { | ||
/** | ||
* Serialize data | ||
* @param from - original data | ||
*/ | ||
serialization(from: any): unknown | PromiseLike<unknown>; | ||
/** | ||
* Deserialize data | ||
* @param serialized - Serialized data | ||
*/ | ||
deserialization(serialized: unknown): unknown | PromiseLike<unknown>; | ||
} | ||
/** | ||
* Make the returning type to `Promise<void>` | ||
* @internal | ||
* @remarks | ||
* Due to the limitation of TypeScript, generic signatures cannot be preserved | ||
* if the function is the top level parameter of this utility type, | ||
* or the function is not returning `Promise<void>`. | ||
*/ | ||
declare type _IgnoreResponse<T> = T extends (...args: infer Args) => unknown ? (...args: Args) => Promise<void> : { | ||
[key in keyof T as T[key] extends Function ? key : never]: T[key] extends (...args: infer Args) => infer Return ? Return extends Promise<void> ? T[key] : (...args: Args) => Promise<void> : never; | ||
}; | ||
/** | ||
* Wrap the AsyncCall instance to send notification. | ||
* @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance | ||
* @example | ||
* const notifyOnly = notify(AsyncCall(...)) | ||
* @public | ||
*/ | ||
declare function notify<T extends object>(instanceOrFnOnInstance: T): _IgnoreResponse<T>; | ||
/** | ||
* Serialization implementation that do nothing | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const NoSerialization: Serialization; | ||
/** | ||
* Create a serialization by JSON.parse/stringify | ||
* | ||
* @param replacerAndReceiver - Replacer and receiver of JSON.parse/stringify | ||
* @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. | ||
* @param undefinedKeepingBehavior - How to keep "undefined" in result of SuccessResponse? | ||
* | ||
* If it is not handled properly, JSON.stringify will emit an invalid JSON RPC object. | ||
* | ||
* Options: | ||
* - `"null"`(**default**): convert it to null. | ||
* - `"keep"`: try to keep it by additional property "undef". | ||
* - `false`: Don't keep it, let it break. | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const JSONSerialization: (replacerAndReceiver?: [(((key: string, value: any) => any) | undefined)?, (((key: string, value: any) => any) | undefined)?], space?: string | number | undefined, undefinedKeepingBehavior?: 'keep' | 'null' | false) => Serialization; | ||
/** | ||
* Wrap the AsyncCall instance to use batch call. | ||
* @param asyncCallInstance - The AsyncCall instance | ||
* @example | ||
* const [batched, send, drop] = batch(AsyncCall(...)) | ||
* batched.call1() // pending | ||
* batched.call2() // pending | ||
* send() // send all pending requests | ||
* drop() // drop all pending requests | ||
* @returns It will return a tuple. | ||
* | ||
* The first item is the batched version of AsyncCall instance passed in. | ||
* | ||
* The second item is a function to send all pending requests. | ||
* | ||
* The third item is a function to drop and reject all pending requests. | ||
* @public | ||
*/ | ||
declare function batch<T extends object>(asyncCallInstance: T): [T, () => void, (error?: unknown) => void]; | ||
/** | ||
* Create a RPC server & client. | ||
@@ -369,2 +369,2 @@ * | ||
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, CallbackBasedChannel, Console, ErrorMapFunction, EventBasedChannel, JSONRPCRequest, JSONSerialization, NoSerialization, Serialization, _AsyncVersionOf, _IgnoreResponse, batch, notify }; | ||
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, CallbackBasedChannel, ConsoleInterface as Console, ConsoleInterface, ErrorMapFunction, EventBasedChannel, JSONRPCRequest, JSONSerialization, NoSerialization, Serialization, _AsyncVersionOf, _IgnoreResponse, batch, notify }; |
/// <reference types="./base.min.d.ts" /> | ||
((r,e)=>{"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((r="undefined"!=typeof globalThis?globalThis:r||self).AsyncCall={})})(this,(function(r){"use strict";class e extends Error{constructor(r,e,t,n){super(e),this.name=r,this.code=t,this.stack=n}}function t(r,e){return e.message+=`Error ${r}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#`+r,e}const n={__proto__:null,Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},o="DOMException:",s=r=>(r+"").replace(/^.+\n.+\n/,""),a=()=>{try{return DOMException}catch(r){}},i=r=>"string"==typeof r,c=r=>"boolean"==typeof r,l=r=>"function"==typeof r,u=r=>"object"==typeof r&&null!==r,f="Error",d=void 0,p=r=>Promise.reject(r),y=Array.isArray,m="2.0",h=(r,e,t,n)=>{r===d&&(r=null),Number.isNaN(e=Math.floor(e))&&(e=-1);const o={jsonrpc:m,id:r,error:{code:e,message:t,data:n}};return $(o.error,"data"),o},g=(r,e,t)=>{const{id:n}=r,{code:o,message:s,data:a}=t(e,r);return h(n,o,s,a)},E=(r="",e=-1)=>t=>{let n=w("",(()=>t.message)),s=w(f,((r=t.constructor)=>l(r)&&r.name));const u=a();return u&&t instanceof u&&(s=o+t.name),(i(t)||"number"==typeof t||c(t)||"bigint"==typeof t)&&(s=f,n=t+""),{code:e,message:n,data:r?{stack:r,type:s}:{type:s}}},b=r=>{if(!u(r))return!1;if(!k(r,"jsonrpc"))return!1;if(r.jsonrpc!==m)return!1;if(k(r,"params")){const e=r.params;if(!y(e)&&!u(e))return!1}return!0},k=(r,e)=>e in r,w=(r,e)=>{try{const t=e();return t===d?r:t+""}catch(e){return r}},$=(r,e)=>{r[e]===d&&delete r[e]},S={serialization:r=>r,deserialization:r=>r},P="AsyncCall/",v=Symbol.for(P+"ignored"),_=Symbol.for(P+"notify"),j=Symbol.for(P+"batch"),x=(r,e)=>r[e][_],z=()=>Math.random().toString(36).slice(2),M=r=>void 0===r||r;r.AsyncCall=(r,w)=>{let P=!0,x=d,N=d;const R=async()=>{try{x=await r}catch(r){N=r,Z("AsyncCall failed to start",r)}finally{P=!1}},{serializer:A=S,key:C="rpc",strict:O=!0,log:T=!0,parameterStructures:W="by-position",preferLocalImplementation:I=!1,idGenerator:J=z,mapError:q,logger:D,channel:F,thenable:L}=w;r instanceof Promise?R():(x=r,P=!1);const[U,G]=(r=>{if(!c(r)){const{methodNotFound:e,unknownMessage:t}=r;return[e,t]}return[r,r]})(O),[B,H,K,Q,V,X]=(r=>{if("all"===r)return[!0,!0,!0,!0,!0,!0];if(!c(r)){const{beCalled:e,localError:t,remoteError:n,type:o,requestReplay:s,sendLocalStack:a}=r;return[M(e),M(t),M(n),"basic"!==o,s,a]}return r?[!0,!0,!0,!0]:[]})(T),{log:Y,error:Z=Y,debug:rr=Y,groupCollapsed:er=Y,groupEnd:tr=Y,warn:nr=Y}=D||console,or=new Map,sr=async r=>{let e;try{if(e=await cr(r),b(e))return await dr(e);if(y(e)&&e.every(b)&&0!==e.length)return Promise.all(e.map(dr));if(G){let r=e.id;return r===d&&(r=null),(r=>h(r,-32600,"Invalid Request"))(r)}return d}catch(r){return H&&Z(r,e,void 0),((r,e)=>{const t=g({},r,e),n=t.error;return n.code=-32700,n.message="Parse error",t})(r,q||E(r&&r.stack))}},ar=async r=>{if(r){if(y(r)){const e=r.filter((r=>r&&k(r,"id")));if(0===e.length)return;return ir(e)}return ir(r)}},ir=r=>A.serialization(r),cr=r=>A.deserialization(r);var lr;if(k(lr=F,"setup")&&l(lr.setup)&&F.setup((r=>sr(r).then(ar)),(r=>{const e=cr(r);return!!b(e)||(r=>Promise.resolve(r))(e).then(b)})),(r=>k(r,"send")&&l(r.send))(F)){const r=F;r.on&&r.on((e=>sr(e).then(ar).then((e=>e&&r.send(e)))))}function ur(r,e,t){return u(r)&&k(r,"stack")&&(r.stack=e.split("\n").reduce(((r,e)=>r.replace(e+"\n","")),""+r.stack)),H&&Z(r),g(t,r,q||E(X?r.stack:d))}async function fr(r,e=!1){e&&(r=[...r]);const t=await ir(r);return F.send(t)}const dr=async r=>{if(k(r,"method")){const e=(async r=>{if(P)await R();else if(N)return ur(N,"",r);let e="";try{const{params:t,method:n,id:o,remoteStack:a}=r,i=n.startsWith("rpc.")?Symbol.for(n):n,c=x&&x[i];if(!l(c))return U?h(o,-32601,"Method not found"):void(H&&rr("Missing method",i,r));const u=y(t)?t:[t];e=s(Error().stack);const f=new Promise((r=>r(c.apply(x,u))));if(B)if(Q){const r=[`${C}.%c${n}%c(${u.map((()=>"%o")).join(", ")}%c)\n%o %c@${o}`,"color: #d2c057","",...u,"",f,"color: gray; font-style: italic;"];V&&r.push((()=>{debugger;return c.apply(x,u)})),a?(er(...r),Y(a),tr()):Y(...r)}else Y(`${C}.${n}(${""+[...u]}) @${o}`);if(await f===v)return;return((r,e)=>{const t={jsonrpc:m,id:r,result:e};return $(t,"id"),t})(o,await f)}catch(t){return ur(t,e,r)}})(r);if(k(r,"id"))return e;try{await e}catch(r){}return d}return(async r=>{let t="",s="",c=0,l=f;if(k(r,"error")){const e=r.error;t=e.message,c=e.code;const n=e.data;s=u(n)&&k(n,"stack")&&i(n.stack)?n.stack:"<remote stack not available>",l=u(n)&&k(n,"type")&&i(n.type)?n.type:f,K&&(Q?Z(`${l}: ${t}(${c}) %c@${r.id}\n%c${s}`,"color: gray",""):Z(`${l}: ${t}(${c}) @${r.id}\n${s}`))}if(null===r.id||r.id===d)return;const{f:[p,y]=[null,null],stack:m=""}=or.get(r.id)||{};p&&y&&(or.delete(r.id),k(r,"error")?y(((r,t,s,i)=>{try{const c=a();if(r.startsWith(o)&&c)return new c(t,r.slice(13));if(r in n){const e=new n[r](t);return e.stack=i,e.code=s,e}return new e(r,t,s,i)}catch(e){return Error(`E${s} ${r}: ${t}\n${i}`)}})(l,t,c,s+"\n аt AsyncCall (rpc) \n"+m)):p(r.result))})(r)};return new Proxy({__proto__:null},{get(r,e){if("then"===e&&(L===d&&nr(t(3,new TypeError("RPC used as Promise: "))),!0!==L))return d;if(i(e)&&r[e])return r[e];const n=r=>(...n)=>{let o=s(Error().stack),a=d;if(e===j&&(a=n.shift(),e=n.shift()),"symbol"==typeof e){const r=Symbol.keyFor(e)||e.description;if(r){if(!r.startsWith("rpc."))return p(new TypeError("Not start with rpc."));e=r}}else if(e.startsWith("rpc."))return p(t(2,new TypeError));return new Promise(((t,s)=>{if(I&&!P&&i(e)){const r=x&&x[e];if(l(r))return t(r(...n))}const c=J(),[f]=n,p=X?o:"",y="by-name"===W&&1===n.length&&u(f)?f:n,h=((r,e,t,n)=>{const o={jsonrpc:m,id:r,method:e,params:t,remoteStack:n};return $(o,"id"),((r,e)=>{r[e]||delete r[e]})(o,"remoteStack"),o})(r?d:c,e,y,p);if(a?(a.push(h),a.r||(a.r=[()=>fr(a,!0),r=>((r,e)=>{for(const t of r)if(k(t,"id")){const r=or.get(t.id);r&&r.f[1](e)}})(a,r)])):fr(h).catch(s),r)return t();or.set(c,{f:[t,s],stack:o})}))},o=n(!1);return o[_]=n(!0),o[_][_]=o[_],i(e)&&Object.defineProperty(r,e,{value:o,configurable:!0}),o}})},r.JSONSerialization=(r=[d,d],e,t="null")=>({serialization(n){if(t&&u(n)&&k(n,"result")&&n.result===d){const r={...n};r.result=null,"keep"===t&&(r.undef=!0),n=r}return JSON.stringify(n,r[0],e)},deserialization(e){const t=JSON.parse(e,r[1]);return u(t)&&k(t,"result")&&null===t.result&&k(t,"undef")&&!0===t.undef&&(t.result=d,delete t.undef),t}}),r.NoSerialization=S,r.batch=r=>{let e=[];return[new Proxy({__proto__:null},{get(t,n){if(i(n)&&t[n])return t[n];const o=(...t)=>r[j](e,n,...t);return(o[_]=(...t)=>r[j][_](e,n,...t))[_]=o[_],i(n)&&Object.defineProperty(t,n,{value:o,configurable:!0}),o}}),(r=e.r)=>r&&r[0](),(r=Error("Aborted"),t=e.r)=>{t&&t[1](r),e=[]}]},r.notify=r=>l(r)?r[_]:new Proxy(r,{get:x}),Object.defineProperty(r,"__esModule",{value:!0})})); | ||
((r,e)=>{"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((r="undefined"!=typeof globalThis?globalThis:r||self).AsyncCall={})})(this,(function(r){"use strict";class e extends Error{constructor(r,e,t,n){super(e),this.name=r,this.code=t,this.stack=n}}const t={},n={},o=[{},{},t,n];function s(r,e){const t=o.indexOf(r);return e.message+=`Error ${t}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#`+t,e}const a={__proto__:null,Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},i="DOMException:",c=r=>(r+"").replace(/^.+\n.+\n/,""),l=()=>{try{return DOMException}catch(r){}},u=r=>"string"==typeof r,f=r=>"boolean"==typeof r,d=r=>"function"==typeof r,p=r=>"object"==typeof r&&null!==r,y="Error",m=void 0,h=r=>Promise.reject(r),g=Array.isArray,E=()=>"() => replay()",b="2.0",k=(r,e,t,n)=>{r===m&&(r=null),Number.isNaN(e=Math.floor(e))&&(e=-1);const o={jsonrpc:b,id:r,error:{code:e,message:t,data:n}};return _(o.error,"data"),o},w=(r,e,t)=>{const{id:n}=r,{code:o,message:s,data:a}=t(e,r);return k(n,o,s,a)},$=(r="",e=-1)=>t=>{let n=v("",(()=>t.message)),o=v(y,((r=t.constructor)=>d(r)&&r.name));const s=l();return s&&t instanceof s&&(o=i+t.name),(u(t)||"number"==typeof t||f(t)||"bigint"==typeof t)&&(o=y,n=t+""),{code:e,message:n,data:r?{stack:r,type:o}:{type:o}}},S=r=>{if(!p(r))return!1;if(!P(r,"jsonrpc"))return!1;if(r.jsonrpc!==b)return!1;if(P(r,"params")){const e=r.params;if(!g(e)&&!p(e))return!1}return!0},P=(r,e)=>e in r,v=(r,e)=>{try{const t=e();return t===m?r:t+""}catch(e){return r}},_=(r,e)=>{r[e]===m&&delete r[e]},j={serialization:r=>r,deserialization:r=>r},x="AsyncCall/",z=Symbol.for(x+"ignored"),M=Symbol.for(x+"notify"),N=Symbol.for(x+"batch"),O=(r,e)=>r[e][M],R=()=>Math.random().toString(36).slice(2),A=r=>void 0===r||r;r.AsyncCall=(r,o)=>{let v=!0,x=m,O=m;const C=async()=>{try{x=await r}catch(r){O=r,tr("AsyncCall failed to start",r)}finally{v=!1}},{serializer:T=j,key:W="rpc",strict:I=!0,log:J=!0,parameterStructures:q="by-position",preferLocalImplementation:D=!1,idGenerator:F=R,mapError:L,logger:U,channel:G,thenable:B}=o;r instanceof Promise?C():(x=r,v=!1);const[H,K]=(r=>{if(!f(r)){const{methodNotFound:e,unknownMessage:t}=r;return[e,t]}return[r,r]})(I),[Q,V,X,Y,Z,rr]=(r=>{if("all"===r)return[!0,!0,!0,!0,!0,!0];if(!f(r)){const{beCalled:e,localError:t,remoteError:n,type:o,requestReplay:s,sendLocalStack:a}=r;return[A(e),A(t),A(n),"basic"!==o,s,a]}return r?[!0,!0,!0,!0]:[]})(J),{log:er,error:tr=er,debug:nr=er,groupCollapsed:or=er,groupEnd:sr=er,warn:ar=er}=U||console,ir=new Map,cr=async r=>{let e;try{if(e=await fr(r),S(e))return await mr(e);if(g(e)&&e.every(S)&&0!==e.length)return Promise.all(e.map(mr));if(K){let r=e.id;return r===m&&(r=null),(r=>k(r,-32600,"Invalid Request"))(r)}return m}catch(r){return V&&tr(r,e,void 0),((r,e)=>{const t=w({},r,e),n=t.error;return n.code=-32700,n.message="Parse error",t})(r,L||$(r&&r.stack))}},lr=async r=>{if(r){if(g(r)){const e=r.filter((r=>r&&P(r,"id")));if(0===e.length)return;return ur(e)}return ur(r)}},ur=r=>T.serialization(r),fr=r=>T.deserialization(r);var dr;if(P(dr=G,"setup")&&d(dr.setup)&&G.setup((r=>cr(r).then(lr)),(r=>{const e=fr(r);return!!S(e)||(r=>Promise.resolve(r))(e).then(S)})),(r=>P(r,"send")&&d(r.send))(G)){const r=G;r.on&&r.on((e=>cr(e).then(lr).then((e=>e&&r.send(e)))))}function pr(r,e,t){return p(r)&&P(r,"stack")&&(r.stack=e.split("\n").reduce(((r,e)=>r.replace(e+"\n","")),""+r.stack)),V&&tr(r),w(t,r,L||$(rr?r.stack:m))}async function yr(r,e=!1){e&&(r=[...r]);const t=await ur(r);return G.send(t)}const mr=async r=>{if(P(r,"method")){const e=(async r=>{if(v)await C();else if(O)return pr(O,"",r);let e="";try{const{params:t,method:n,id:o,remoteStack:s}=r,a=n.startsWith("rpc.")?Symbol.for(n):n,i=x&&x[a];if(!d(i))return H?k(o,-32601,"Method not found"):void(V&&nr("Missing method",a,r));const l=g(t)?t:[t];e=c(Error().stack);const u=new Promise((r=>r(i.apply(x,l))));if(Q)if(Y){const r=[`${W}.%c${n}%c(${l.map((()=>"%o")).join(", ")}%c)\n%o %c@${o}`,"color: #d2c057","",...l,"",u,"color: gray; font-style: italic;"];if(Z){const e=()=>{debugger;return i.apply(x,l)};e.toString=E,r.push(e)}s?(or(...r),er(s),sr()):er(...r)}else er(`${W}.${n}(${""+[...l]}) @${o}`);if(await u===z)return;return((r,e)=>{const t={jsonrpc:b,id:r,result:e};return _(t,"id"),t})(o,await u)}catch(t){return pr(t,e,r)}})(r);if(P(r,"id"))return e;try{await e}catch(r){}return m}return(async r=>{let t="",n="",o=0,s=y;if(P(r,"error")){const e=r.error;t=e.message,o=e.code;const a=e.data;n=p(a)&&P(a,"stack")&&u(a.stack)?a.stack:"<remote stack not available>",s=p(a)&&P(a,"type")&&u(a.type)?a.type:y,X&&(Y?tr(`${s}: ${t}(${o}) %c@${r.id}\n%c${n}`,"color: gray",""):tr(`${s}: ${t}(${o}) @${r.id}\n${n}`))}if(null===r.id||r.id===m)return;const{f:[c,f]=[null,null],stack:d=""}=ir.get(r.id)||{};c&&f&&(ir.delete(r.id),P(r,"error")?f(((r,t,n,o)=>{try{const s=l();if(r.startsWith(i)&&s)return new s(t,r.slice(13));if(r in a){const e=new a[r](t);return e.stack=o,e.code=n,e}return new e(r,t,n,o)}catch(e){return Error(`E${n} ${r}: ${t}\n${o}`)}})(s,t,o,n+"\n аt AsyncCall (rpc) \n"+d)):c(r.result))})(r)};return new Proxy({__proto__:null},{get(r,e){if("then"===e&&(B===m&&ar(s(n,new TypeError("RPC used as Promise: "))),!0!==B))return m;if(u(e)&&r[e])return r[e];const o=r=>(...n)=>{let o=c(Error().stack),a=m;if(e===N&&(a=n.shift(),e=n.shift()),"symbol"==typeof e){const r=Symbol.keyFor(e)||e.description;if(r){if(!r.startsWith("rpc."))return h(new TypeError("Not start with rpc."));e=r}}else if(e.startsWith("rpc."))return h(s(t,new TypeError));return new Promise(((t,s)=>{if(D&&!v&&u(e)){const r=x&&x[e];if(d(r))return t(r(...n))}const i=F(),[c]=n,l=rr?o:"",f="by-name"===q&&1===n.length&&p(c)?c:n,y=((r,e,t,n)=>{const o={jsonrpc:b,id:r,method:e,params:t,remoteStack:n};return _(o,"id"),((r,e)=>{r[e]||delete r[e]})(o,"remoteStack"),o})(r?m:i,e,f,l);if(a?(a.push(y),a.r||(a.r=[()=>yr(a,!0),r=>((r,e)=>{for(const t of r)if(P(t,"id")){const r=ir.get(t.id);r&&r.f[1](e)}})(a,r)])):yr(y).catch(s),r)return t();ir.set(i,{f:[t,s],stack:o})}))},a=o(!1);return a[M]=o(!0),a[M][M]=a[M],u(e)&&Object.defineProperty(r,e,{value:a,configurable:!0}),a}})},r.JSONSerialization=(r=[m,m],e,t="null")=>({serialization(n){if(t&&p(n)&&P(n,"result")&&n.result===m){const r={...n};r.result=null,"keep"===t&&(r.undef=!0),n=r}return JSON.stringify(n,r[0],e)},deserialization(e){const t=JSON.parse(e,r[1]);return p(t)&&P(t,"result")&&null===t.result&&P(t,"undef")&&!0===t.undef&&(t.result=m,delete t.undef),t}}),r.NoSerialization=j,r.batch=r=>{let e=[];return[new Proxy({__proto__:null},{get(t,n){if(u(n)&&t[n])return t[n];const o=(...t)=>r[N](e,n,...t);return(o[M]=(...t)=>r[N][M](e,n,...t))[M]=o[M],u(n)&&Object.defineProperty(t,n,{value:o,configurable:!0}),o}}),(r=e.r)=>r&&r[0](),(r=Error("Aborted"),t=e.r)=>{t&&t[1](r),e=[]}]},r.notify=r=>d(r)?r[M]:new Proxy(r,{get:O}),Object.defineProperty(r,"__esModule",{value:!0})})); | ||
//# sourceMappingURL=base.min.js.map |
/** | ||
* ! This file MUST NOT contain any import statement. | ||
* ! This file is part of public API of this package (for Deno users). | ||
*/ | ||
/** | ||
* This interface represents a "on message" - "send response" model. | ||
@@ -37,103 +41,3 @@ * @remarks | ||
} | ||
/** | ||
* Serialize and deserialize of the JSON RPC payload | ||
* @public | ||
*/ | ||
interface Serialization { | ||
/** | ||
* Serialize data | ||
* @param from - original data | ||
*/ | ||
serialization(from: any): unknown | PromiseLike<unknown>; | ||
/** | ||
* Deserialize data | ||
* @param serialized - Serialized data | ||
*/ | ||
deserialization(serialized: unknown): unknown | PromiseLike<unknown>; | ||
} | ||
/** | ||
* Serialization implementation that do nothing | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const NoSerialization: Serialization; | ||
/** | ||
* Create a serialization by JSON.parse/stringify | ||
* | ||
* @param replacerAndReceiver - Replacer and receiver of JSON.parse/stringify | ||
* @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. | ||
* @param undefinedKeepingBehavior - How to keep "undefined" in result of SuccessResponse? | ||
* | ||
* If it is not handled properly, JSON.stringify will emit an invalid JSON RPC object. | ||
* | ||
* Options: | ||
* - `"null"`(**default**): convert it to null. | ||
* - `"keep"`: try to keep it by additional property "undef". | ||
* - `false`: Don't keep it, let it break. | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const JSONSerialization: (replacerAndReceiver?: [(((key: string, value: any) => any) | undefined)?, (((key: string, value: any) => any) | undefined)?], space?: string | number | undefined, undefinedKeepingBehavior?: 'keep' | 'null' | false) => Serialization; | ||
/** | ||
* The minimal Console interface that AsyncCall needs. | ||
* @public | ||
* @remarks | ||
* The method not provided will use "log" as it's fallback. | ||
*/ | ||
interface Console { | ||
warn?(...args: unknown[]): void; | ||
debug?(...args: unknown[]): void; | ||
log(...args: unknown[]): void; | ||
groupCollapsed?(...args: unknown[]): void; | ||
groupEnd?(...args: unknown[]): void; | ||
error?(...args: unknown[]): void; | ||
} | ||
/** | ||
* Make the returning type to `Promise<void>` | ||
* @internal | ||
* @remarks | ||
* Due to the limitation of TypeScript, generic signatures cannot be preserved | ||
* if the function is the top level parameter of this utility type, | ||
* or the function is not returning `Promise<void>`. | ||
*/ | ||
declare type _IgnoreResponse<T> = T extends (...args: infer Args) => unknown ? (...args: Args) => Promise<void> : { | ||
[key in keyof T as T[key] extends Function ? key : never]: T[key] extends (...args: infer Args) => infer Return ? Return extends Promise<void> ? T[key] : (...args: Args) => Promise<void> : never; | ||
}; | ||
/** | ||
* Wrap the AsyncCall instance to send notification. | ||
* @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance | ||
* @example | ||
* const notifyOnly = notify(AsyncCall(...)) | ||
* @public | ||
*/ | ||
declare function notify<T extends object>(instanceOrFnOnInstance: T): _IgnoreResponse<T>; | ||
/** | ||
* Wrap the AsyncCall instance to use batch call. | ||
* @param asyncCallInstance - The AsyncCall instance | ||
* @example | ||
* const [batched, send, drop] = batch(AsyncCall(...)) | ||
* batched.call1() // pending | ||
* batched.call2() // pending | ||
* send() // send all pending requests | ||
* drop() // drop all pending requests | ||
* @returns It will return a tuple. | ||
* | ||
* The first item is the batched version of AsyncCall instance passed in. | ||
* | ||
* The second item is a function to send all pending requests. | ||
* | ||
* The third item is a function to drop and reject all pending requests. | ||
* @public | ||
*/ | ||
declare function batch<T extends object>(asyncCallInstance: T): [T, () => void, (error?: unknown) => void]; | ||
/** | ||
* See the document at https://github.com/Jack-Works/async-call/ | ||
*/ | ||
/** | ||
* Log options of AsyncCall | ||
@@ -222,3 +126,3 @@ * @remarks | ||
* | ||
* - {@link https://github.com/jack-works/async-call-rpc#web-deno-and-node-bson BSONSerialization} (use the {@link https://npmjs.org/bson | bson} as the serializer) | ||
* - {@link https://github.com/jack-works/async-call-rpc#web-deno-and-node-bson | BSONSerialization} (use the {@link https://npmjs.org/bson | bson} as the serializer) | ||
* | ||
@@ -231,6 +135,6 @@ * @defaultValue {@link NoSerialization} | ||
* @remarks | ||
* See {@link Console} | ||
* See {@link ConsoleInterface} | ||
* @defaultValue globalThis.console | ||
*/ | ||
logger?: Console; | ||
logger?: ConsoleInterface; | ||
/** | ||
@@ -349,2 +253,98 @@ * The message channel to exchange messages between server and client | ||
/** | ||
* The minimal Console interface that AsyncCall needs. | ||
* @public | ||
* @remarks | ||
* The method not provided will use "log" as it's fallback. | ||
*/ | ||
interface ConsoleInterface { | ||
warn?(...args: unknown[]): void; | ||
debug?(...args: unknown[]): void; | ||
log(...args: unknown[]): void; | ||
groupCollapsed?(...args: unknown[]): void; | ||
groupEnd?(...args: unknown[]): void; | ||
error?(...args: unknown[]): void; | ||
} | ||
/** | ||
* Serialize and deserialize of the JSON RPC payload | ||
* @public | ||
*/ | ||
interface Serialization { | ||
/** | ||
* Serialize data | ||
* @param from - original data | ||
*/ | ||
serialization(from: any): unknown | PromiseLike<unknown>; | ||
/** | ||
* Deserialize data | ||
* @param serialized - Serialized data | ||
*/ | ||
deserialization(serialized: unknown): unknown | PromiseLike<unknown>; | ||
} | ||
/** | ||
* Make the returning type to `Promise<void>` | ||
* @internal | ||
* @remarks | ||
* Due to the limitation of TypeScript, generic signatures cannot be preserved | ||
* if the function is the top level parameter of this utility type, | ||
* or the function is not returning `Promise<void>`. | ||
*/ | ||
declare type _IgnoreResponse<T> = T extends (...args: infer Args) => unknown ? (...args: Args) => Promise<void> : { | ||
[key in keyof T as T[key] extends Function ? key : never]: T[key] extends (...args: infer Args) => infer Return ? Return extends Promise<void> ? T[key] : (...args: Args) => Promise<void> : never; | ||
}; | ||
/** | ||
* Wrap the AsyncCall instance to send notification. | ||
* @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance | ||
* @example | ||
* const notifyOnly = notify(AsyncCall(...)) | ||
* @public | ||
*/ | ||
declare function notify<T extends object>(instanceOrFnOnInstance: T): _IgnoreResponse<T>; | ||
/** | ||
* Serialization implementation that do nothing | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const NoSerialization: Serialization; | ||
/** | ||
* Create a serialization by JSON.parse/stringify | ||
* | ||
* @param replacerAndReceiver - Replacer and receiver of JSON.parse/stringify | ||
* @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. | ||
* @param undefinedKeepingBehavior - How to keep "undefined" in result of SuccessResponse? | ||
* | ||
* If it is not handled properly, JSON.stringify will emit an invalid JSON RPC object. | ||
* | ||
* Options: | ||
* - `"null"`(**default**): convert it to null. | ||
* - `"keep"`: try to keep it by additional property "undef". | ||
* - `false`: Don't keep it, let it break. | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const JSONSerialization: (replacerAndReceiver?: [(((key: string, value: any) => any) | undefined)?, (((key: string, value: any) => any) | undefined)?], space?: string | number | undefined, undefinedKeepingBehavior?: 'keep' | 'null' | false) => Serialization; | ||
/** | ||
* Wrap the AsyncCall instance to use batch call. | ||
* @param asyncCallInstance - The AsyncCall instance | ||
* @example | ||
* const [batched, send, drop] = batch(AsyncCall(...)) | ||
* batched.call1() // pending | ||
* batched.call2() // pending | ||
* send() // send all pending requests | ||
* drop() // drop all pending requests | ||
* @returns It will return a tuple. | ||
* | ||
* The first item is the batched version of AsyncCall instance passed in. | ||
* | ||
* The second item is a function to send all pending requests. | ||
* | ||
* The third item is a function to drop and reject all pending requests. | ||
* @public | ||
*/ | ||
declare function batch<T extends object>(asyncCallInstance: T): [T, () => void, (error?: unknown) => void]; | ||
/** | ||
* Create a RPC server & client. | ||
@@ -427,2 +427,2 @@ * | ||
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, AsyncGeneratorCall, CallbackBasedChannel, Console, ErrorMapFunction, EventBasedChannel, JSONRPCRequest, JSONSerialization, NoSerialization, Serialization, _AsyncGeneratorVersionOf, _AsyncVersionOf, _IgnoreResponse, _IteratorLikeToAsyncGenerator, _IteratorOrIterableFunction, batch, notify }; | ||
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, AsyncGeneratorCall, CallbackBasedChannel, ConsoleInterface as Console, ConsoleInterface, ErrorMapFunction, EventBasedChannel, JSONRPCRequest, JSONSerialization, NoSerialization, Serialization, _AsyncGeneratorVersionOf, _AsyncVersionOf, _IgnoreResponse, _IteratorLikeToAsyncGenerator, _IteratorOrIterableFunction, batch, notify }; |
741
out/full.js
@@ -9,12 +9,21 @@ /// <reference types="./full.d.ts" /> | ||
class CustomError extends Error { | ||
constructor(name, message, code, stack) { | ||
super(message); | ||
this.name = name; | ||
this.code = code; | ||
this.stack = stack; | ||
constructor( name, message, code, stack) { | ||
super(message);this.name = name;this.code = code;this.stack = stack; | ||
} | ||
} | ||
const Err_Cannot_find_a_running_iterator_with_given_ID = {}; | ||
const Err_Only_string_can_be_the_RPC_method_name = {}; | ||
const Err_Cannot_call_method_starts_with_rpc_dot_directly = {}; | ||
const Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options = {}; | ||
const Messages = [ | ||
Err_Cannot_find_a_running_iterator_with_given_ID, | ||
Err_Only_string_can_be_the_RPC_method_name, | ||
Err_Cannot_call_method_starts_with_rpc_dot_directly, | ||
Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options, | ||
]; | ||
// https://github.com/Jack-Works/async-call-rpc/wiki/Error-messages | ||
function makeHostedMessage(id, error) { | ||
error.message += `Error ${id}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#` + id; | ||
return error; | ||
const n = Messages.indexOf(id); | ||
error.message += `Error ${n}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#` + n; | ||
return error | ||
} | ||
@@ -43,5 +52,4 @@ // ! side effect | ||
const name = type.slice(DOMExceptionHeader.length); | ||
return new E(message, name); | ||
} | ||
else if (type in errors) { | ||
return new E(message, name) | ||
} else if (type in errors) { | ||
const e = new errors[type](message); | ||
@@ -51,11 +59,9 @@ e.stack = stack; | ||
e.code = code; | ||
return e; | ||
return e | ||
} else { | ||
return new CustomError(type, message, code, stack) | ||
} | ||
else { | ||
return new CustomError(type, message, code, stack); | ||
} | ||
} catch (e2) { | ||
return new Error(`E${code} ${type}: ${message}\n${stack}`) | ||
} | ||
catch (_a) { | ||
return new Error(`E${code} ${type}: ${message}\n${stack}`); | ||
} | ||
}; | ||
@@ -67,5 +73,4 @@ const removeStackHeader = (stack) => String(stack).replace(/^.+\n.+\n/, ''); | ||
// @ts-ignore | ||
return DOMException; | ||
} | ||
catch (_a) { } | ||
return DOMException | ||
} catch (e3) {} | ||
}); | ||
@@ -83,4 +88,18 @@ | ||
const isArray = Array.isArray; | ||
const replayFunction = () => '() => replay()'; | ||
const jsonrpc = '2.0'; | ||
const Request = (id, method, params, remoteStack) => { | ||
@@ -90,18 +109,38 @@ const x = { jsonrpc, id, method, params, remoteStack }; | ||
deleteFalsy(x, 'remoteStack'); | ||
return x; | ||
return x | ||
}; | ||
/** | ||
* JSONRPC SuccessResponse object. | ||
*/ | ||
const SuccessResponse = (id, result) => { | ||
const x = { jsonrpc, id, result }; | ||
deleteUndefined(x, 'id'); | ||
return x; | ||
return x | ||
}; | ||
/** | ||
* JSONRPC ErrorResponse object. | ||
* @public | ||
*/ | ||
const ErrorResponse = (id, code, message, data) => { | ||
if (id === undefined$1) | ||
id = null; | ||
if (id === undefined$1) id = null; | ||
code = Math.floor(code); | ||
if (Number.isNaN(code)) | ||
code = -1; | ||
if (Number.isNaN(code)) code = -1; | ||
const x = { jsonrpc, id, error: { code, message, data } }; | ||
deleteUndefined(x.error, 'data'); | ||
return x; | ||
return x | ||
}; | ||
@@ -111,8 +150,9 @@ // Pre defined error in section 5.1 | ||
const ErrorResponseParseError = (e, mapper) => { | ||
const obj = ErrorResponseMapped({}, e, mapper); | ||
const o = obj.error; | ||
const obj = ErrorResponseMapped({} , e, mapper); | ||
const o = obj.error; | ||
o.code = -32700; | ||
o.message = 'Parse error'; | ||
return obj; | ||
return obj | ||
}; | ||
// Not using. | ||
@@ -123,13 +163,18 @@ // InvalidParams -32602 'Invalid params' | ||
const ErrorResponseMethodNotFound = (id) => ErrorResponse(id, -32601, 'Method not found'); | ||
const ErrorResponseMapped = (request, e, mapper) => { | ||
const { id } = request; | ||
const { code, message, data } = mapper(e, request); | ||
return ErrorResponse(id, code, message, data); | ||
return ErrorResponse(id, code, message, data) | ||
}; | ||
const defaultErrorMapper = (stack = '', code = -1) => (e) => { | ||
let message = toString('', () => e.message); | ||
let type = toString(ERROR, (ctor = e.constructor) => isFunction(ctor) && ctor.name); | ||
let message = toString('', () => (e ).message); | ||
let type = toString(ERROR, (ctor = (e ).constructor) => isFunction(ctor) && ctor.name); | ||
const E = globalDOMException(); | ||
if (E && e instanceof E) | ||
type = DOMExceptionHeader + e.name; | ||
if (E && e instanceof E) type = DOMExceptionHeader + e.name; | ||
if (isString(e) || typeof e === 'number' || isBoolean(e) || typeof e === 'bigint') { | ||
@@ -140,40 +185,49 @@ type = ERROR; | ||
const data = stack ? { stack, type } : { type }; | ||
return { code, message, data }; | ||
return { code, message, data } | ||
}; | ||
/** | ||
* A JSONRPC response object | ||
*/ | ||
const isJSONRPCObject = (data) => { | ||
if (!isObject(data)) | ||
return false; | ||
if (!hasKey(data, 'jsonrpc')) | ||
return false; | ||
if (data.jsonrpc !== jsonrpc) | ||
return false; | ||
if (!isObject(data)) return false | ||
if (!hasKey(data, 'jsonrpc')) return false | ||
if (data.jsonrpc !== jsonrpc) return false | ||
if (hasKey(data, 'params')) { | ||
const params = data.params; | ||
if (!isArray(params) && !isObject(params)) | ||
return false; | ||
const params = (data ).params; | ||
if (!isArray(params) && !isObject(params)) return false | ||
} | ||
return true; | ||
return true | ||
}; | ||
const hasKey = (obj, key) => key in obj; | ||
const hasKey = ( | ||
obj, | ||
key, | ||
) => key in obj; | ||
const toString = (_default, val) => { | ||
try { | ||
const v = val(); | ||
if (v === undefined$1) | ||
return _default; | ||
return String(v); | ||
if (v === undefined$1) return _default | ||
return String(v) | ||
} catch (e2) { | ||
return _default | ||
} | ||
catch (_a) { | ||
return _default; | ||
} | ||
}; | ||
const deleteUndefined = (x, key) => { | ||
if (x[key] === undefined$1) | ||
delete x[key]; | ||
if (x[key] === undefined$1) delete x[key]; | ||
}; | ||
const deleteFalsy = (x, key) => { | ||
if (!x[key]) | ||
delete x[key]; | ||
if (!x[key]) delete x[key]; | ||
}; | ||
//#region Serialization | ||
/** | ||
@@ -186,8 +240,9 @@ * Serialization implementation that do nothing | ||
serialization(from) { | ||
return from; | ||
return from | ||
}, | ||
deserialization(serialized) { | ||
return serialized; | ||
return serialized | ||
}, | ||
}; | ||
/** | ||
@@ -209,6 +264,10 @@ * Create a serialization by JSON.parse/stringify | ||
*/ | ||
const JSONSerialization = (replacerAndReceiver = [ | ||
undefined$1, | ||
undefined$1, | ||
], space, undefinedKeepingBehavior = 'null') => ({ | ||
const JSONSerialization = ( | ||
replacerAndReceiver = [ | ||
undefined$1, | ||
undefined$1, | ||
], | ||
space, | ||
undefinedKeepingBehavior = 'null', | ||
) => ({ | ||
serialization(from) { | ||
@@ -218,19 +277,20 @@ if (undefinedKeepingBehavior && isObject(from) && hasKey(from, 'result') && from.result === undefined$1) { | ||
alt.result = null; | ||
if (undefinedKeepingBehavior === 'keep') | ||
alt.undef = true; | ||
if (undefinedKeepingBehavior === 'keep') (alt ).undef = true; | ||
from = alt; | ||
} | ||
return JSON.stringify(from, replacerAndReceiver[0], space); | ||
return JSON.stringify(from, replacerAndReceiver[0], space) | ||
}, | ||
deserialization(serialized) { | ||
const result = JSON.parse(serialized, replacerAndReceiver[1]); | ||
if (isObject(result) && | ||
const result = JSON.parse(serialized , replacerAndReceiver[1]); | ||
if ( | ||
isObject(result) && | ||
hasKey(result, 'result') && | ||
result.result === null && | ||
hasKey(result, 'undef') && | ||
result.undef === true) { | ||
result.undef === true | ||
) { | ||
result.result = undefined$1; | ||
delete result.undef; | ||
} | ||
return result; | ||
return result | ||
}, | ||
@@ -240,9 +300,28 @@ }); | ||
const i = 'AsyncCall/'; | ||
const i$1 = 'AsyncCall/'; | ||
// ! side effect | ||
const AsyncCallIgnoreResponse = Symbol.for(i + 'ignored'); | ||
const AsyncCallNotify = Symbol.for(i + 'notify'); | ||
const AsyncCallBatch = Symbol.for(i + 'batch'); | ||
const AsyncCallIgnoreResponse = Symbol.for(i$1 + 'ignored'); | ||
const AsyncCallNotify = Symbol.for(i$1 + 'notify'); | ||
const AsyncCallBatch = Symbol.for(i$1 + 'batch'); | ||
/** | ||
* Make the returning type to `Promise<void>` | ||
* @internal | ||
* @remarks | ||
* Due to the limitation of TypeScript, generic signatures cannot be preserved | ||
* if the function is the top level parameter of this utility type, | ||
* or the function is not returning `Promise<void>`. | ||
*/ | ||
/** | ||
* Wrap the AsyncCall instance to send notification. | ||
@@ -254,9 +333,9 @@ * @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance | ||
*/ | ||
function notify(instanceOrFnOnInstance) { | ||
if (isFunction(instanceOrFnOnInstance)) | ||
return instanceOrFnOnInstance[AsyncCallNotify]; | ||
return new Proxy(instanceOrFnOnInstance, { get: notifyTrap }); | ||
if (isFunction(instanceOrFnOnInstance)) return (instanceOrFnOnInstance )[AsyncCallNotify] | ||
return new Proxy(instanceOrFnOnInstance, { get: notifyTrap }) | ||
} | ||
const notifyTrap = (target, p) => { | ||
return target[p][AsyncCallNotify]; | ||
return target[p][AsyncCallNotify] | ||
}; | ||
@@ -283,18 +362,17 @@ | ||
function batch(asyncCallInstance) { | ||
let queue = []; | ||
let queue = []; | ||
return [ | ||
new Proxy({ __proto__: null }, { | ||
new Proxy({ __proto__: null } , { | ||
get(cache, p) { | ||
if (isString(p) && cache[p]) | ||
return cache[p]; | ||
if (isString(p) && cache[p]) return cache[p] | ||
// @ts-ignore | ||
const f = (...args) => asyncCallInstance[AsyncCallBatch](queue, p, ...args); | ||
// @ts-ignore | ||
f[AsyncCallNotify] = (...args) => | ||
f[AsyncCallNotify] = (...args) => | ||
// @ts-ignore | ||
asyncCallInstance[AsyncCallBatch][AsyncCallNotify](queue, p, ...args); | ||
// @ts-ignore | ||
asyncCallInstance[AsyncCallBatch][AsyncCallNotify](queue, p, ...args); | ||
// @ts-ignore | ||
f[AsyncCallNotify][AsyncCallNotify] = f[AsyncCallNotify]; | ||
isString(p) && Object.defineProperty(cache, p, { value: f, configurable: true }); | ||
return f; | ||
return f | ||
}, | ||
@@ -307,3 +385,3 @@ }), | ||
}, | ||
]; | ||
] | ||
} | ||
@@ -314,5 +392,13 @@ | ||
const undefinedToTrue = (x) => (x === void 0 ? true : x); | ||
const normalizeLogOptions = (log) => { | ||
if (log === 'all') | ||
return [true, true, true, true, true, true]; | ||
if (log === 'all') return [true, true, true, true, true, true] | ||
if (!isBoolean(log)) { | ||
@@ -327,20 +413,17 @@ const { beCalled, localError, remoteError, type, requestReplay, sendLocalStack } = log; | ||
sendLocalStack, | ||
]; | ||
] | ||
} | ||
if (log) | ||
return [true, true, true, true]; | ||
return []; | ||
if (log) return [true, true, true, true] | ||
return [] | ||
}; | ||
const normalizeStrictOptions = (strict) => { | ||
if (!isBoolean(strict)) { | ||
const { methodNotFound, unknownMessage } = strict; | ||
return [methodNotFound, unknownMessage]; | ||
return [methodNotFound, unknownMessage] | ||
} | ||
return [strict, strict]; | ||
return [strict, strict] | ||
}; | ||
/** | ||
* See the document at https://github.com/Jack-Works/async-call/ | ||
*/ | ||
/** | ||
* Create a RPC server & client. | ||
@@ -363,3 +446,6 @@ * | ||
*/ | ||
function AsyncCall(thisSideImplementation, options) { | ||
function AsyncCall( | ||
thisSideImplementation, | ||
options, | ||
) { | ||
let isThisSideImplementationPending = true; | ||
@@ -372,14 +458,25 @@ let resolvedThisSideImplementationValue = undefined$1; | ||
resolvedThisSideImplementationValue = await thisSideImplementation; | ||
} | ||
catch (e) { | ||
} catch (e) { | ||
rejectedThisSideImplementation = e; | ||
console_error('AsyncCall failed to start', e); | ||
} | ||
finally { | ||
} finally { | ||
isThisSideImplementationPending = false; | ||
} | ||
}; | ||
const { serializer = NoSerialization, key: logKey = 'rpc', strict = true, log = true, parameterStructures = 'by-position', preferLocalImplementation = false, idGenerator = generateRandomID, mapError, logger, channel, thenable, } = options; | ||
if (thisSideImplementation instanceof Promise) | ||
awaitThisSideImplementation(); | ||
const { | ||
serializer = NoSerialization, | ||
key: logKey = 'rpc', | ||
strict = true, | ||
log = true, | ||
parameterStructures = 'by-position', | ||
preferLocalImplementation = false, | ||
idGenerator = generateRandomID, | ||
mapError, | ||
logger, | ||
channel, | ||
thenable, | ||
} = options; | ||
if (thisSideImplementation instanceof Promise) awaitThisSideImplementation(); | ||
else { | ||
@@ -389,13 +486,27 @@ resolvedThisSideImplementationValue = thisSideImplementation; | ||
} | ||
const [banMethodNotFound, banUnknownMessage] = normalizeStrictOptions(strict); | ||
const [log_beCalled, log_localError, log_remoteError, log_pretty, log_requestReplay, log_sendLocalStack,] = normalizeLogOptions(log); | ||
const { log: console_log, error: console_error = console_log, debug: console_debug = console_log, groupCollapsed: console_groupCollapsed = console_log, groupEnd: console_groupEnd = console_log, warn: console_warn = console_log, } = (logger || console); | ||
const [ | ||
log_beCalled, | ||
log_localError, | ||
log_remoteError, | ||
log_pretty, | ||
log_requestReplay, | ||
log_sendLocalStack, | ||
] = normalizeLogOptions(log); | ||
const { | ||
log: console_log, | ||
error: console_error = console_log, | ||
debug: console_debug = console_log, | ||
groupCollapsed: console_groupCollapsed = console_log, | ||
groupEnd: console_groupEnd = console_log, | ||
warn: console_warn = console_log, | ||
} = (logger || console); | ||
const requestContext = new Map(); | ||
const onRequest = async (data) => { | ||
if (isThisSideImplementationPending) | ||
await awaitThisSideImplementation(); | ||
if (isThisSideImplementationPending) await awaitThisSideImplementation(); | ||
else { | ||
// not pending | ||
if (rejectedThisSideImplementation) | ||
return makeErrorObject(rejectedThisSideImplementation, '', data); | ||
if (rejectedThisSideImplementation) return makeErrorObject(rejectedThisSideImplementation, '', data) | ||
} | ||
@@ -406,12 +517,10 @@ let frameworkStack = ''; | ||
// ? We're mapping any method starts with 'rpc.' to a Symbol.for | ||
const key = (method.startsWith('rpc.') ? Symbol.for(method) : method); | ||
const executor = resolvedThisSideImplementationValue && resolvedThisSideImplementationValue[key]; | ||
const key = (method.startsWith('rpc.') ? Symbol.for(method) : method); | ||
const executor = | ||
resolvedThisSideImplementationValue && (resolvedThisSideImplementationValue )[key]; | ||
if (!isFunction(executor)) { | ||
if (!banMethodNotFound) { | ||
if (log_localError) | ||
console_debug('Missing method', key, data); | ||
return; | ||
} | ||
else | ||
return ErrorResponseMethodNotFound(req_id); | ||
if (log_localError) console_debug('Missing method', key, data); | ||
return | ||
} else return ErrorResponseMethodNotFound(req_id) | ||
} | ||
@@ -432,7 +541,9 @@ const args = isArray(params) ? params : [params]; | ||
]; | ||
if (log_requestReplay) | ||
logArgs.push(() => { | ||
debugger; | ||
return executor.apply(resolvedThisSideImplementationValue, args); | ||
}); | ||
if (log_requestReplay) { | ||
// This function will be logged to the console so it must be 1 line | ||
// prettier-ignore | ||
const replay = () => { debugger; return executor.apply(resolvedThisSideImplementationValue, args) }; | ||
replay.toString = replayFunction; | ||
logArgs.push(replay); | ||
} | ||
if (remoteStack) { | ||
@@ -442,20 +553,17 @@ console_groupCollapsed(...logArgs); | ||
console_groupEnd(); | ||
} | ||
else | ||
console_log(...logArgs); | ||
} | ||
else | ||
console_log(`${logKey}.${method}(${[...args].toString()}) @${req_id}`); | ||
} else console_log(...logArgs); | ||
} else console_log(`${logKey}.${method}(${[...args].toString()}) @${req_id}`); | ||
} | ||
const result = await promise; | ||
if (result === AsyncCallIgnoreResponse) | ||
return; | ||
return SuccessResponse(req_id, await promise); | ||
if (result === AsyncCallIgnoreResponse) return | ||
return SuccessResponse(req_id, await promise) | ||
} catch (e) { | ||
return makeErrorObject(e, frameworkStack, data) | ||
} | ||
catch (e) { | ||
return makeErrorObject(e, frameworkStack, data); | ||
} | ||
}; | ||
const onResponse = async (data) => { | ||
let errorMessage = '', remoteErrorStack = '', errorCode = 0, errorType = ERROR; | ||
let errorMessage = '', | ||
remoteErrorStack = '', | ||
errorCode = 0, | ||
errorType = ERROR; | ||
if (hasKey(data, 'error')) { | ||
@@ -466,30 +574,36 @@ const e = data.error; | ||
const detail = e.data; | ||
if (isObject(detail) && hasKey(detail, 'stack') && isString(detail.stack)) | ||
remoteErrorStack = detail.stack; | ||
else | ||
remoteErrorStack = '<remote stack not available>'; | ||
if (isObject(detail) && hasKey(detail, 'type') && isString(detail.type)) | ||
errorType = detail.type; | ||
else | ||
errorType = ERROR; | ||
if (isObject(detail) && hasKey(detail, 'stack') && isString(detail.stack)) remoteErrorStack = detail.stack; | ||
else remoteErrorStack = '<remote stack not available>'; | ||
if (isObject(detail) && hasKey(detail, 'type') && isString(detail.type)) errorType = detail.type; | ||
else errorType = ERROR; | ||
if (log_remoteError) | ||
log_pretty | ||
? console_error(`${errorType}: ${errorMessage}(${errorCode}) %c@${data.id}\n%c${remoteErrorStack}`, 'color: gray', '') | ||
? console_error( | ||
`${errorType}: ${errorMessage}(${errorCode}) %c@${data.id}\n%c${remoteErrorStack}`, | ||
'color: gray', | ||
'', | ||
) | ||
: console_error(`${errorType}: ${errorMessage}(${errorCode}) @${data.id}\n${remoteErrorStack}`); | ||
} | ||
if (data.id === null || data.id === undefined$1) | ||
return; | ||
if (data.id === null || data.id === undefined$1) return | ||
const { f: [resolve, reject] = [null, null], stack: localErrorStack = '' } = requestContext.get(data.id) || {}; | ||
if (!resolve || !reject) | ||
return; // drop this response | ||
if (!resolve || !reject) return // drop this response | ||
requestContext.delete(data.id); | ||
if (hasKey(data, 'error')) { | ||
reject(RecoverError(errorType, errorMessage, errorCode, | ||
// ? We use \u0430 which looks like "a" to prevent browser think "at AsyncCall" is a real stack | ||
remoteErrorStack + '\n \u0430t AsyncCall (rpc) \n' + localErrorStack)); | ||
} | ||
else { | ||
reject( | ||
RecoverError( | ||
errorType, | ||
errorMessage, | ||
errorCode, | ||
// ? We use \u0430 which looks like "a" to prevent browser think "at AsyncCall" is a real stack | ||
remoteErrorStack + '\n \u0430t AsyncCall (rpc) \n' + localErrorStack, | ||
), | ||
); | ||
} else { | ||
resolve(data.result); | ||
} | ||
return; | ||
return | ||
}; | ||
@@ -502,38 +616,29 @@ const rawMessageReceiver = async (_) => { | ||
if (isJSONRPCObject(data)) { | ||
return (result = await handleSingleMessage(data)); | ||
} | ||
else if (isArray(data) && data.every(isJSONRPCObject) && data.length !== 0) { | ||
return Promise.all(data.map(handleSingleMessage)); | ||
} | ||
else { | ||
return (result = await handleSingleMessage(data)) | ||
} else if (isArray(data) && data.every(isJSONRPCObject) && data.length !== 0) { | ||
return Promise.all(data.map(handleSingleMessage)) | ||
} else { | ||
if (banUnknownMessage) { | ||
let id = data.id; | ||
if (id === undefined$1) | ||
id = null; | ||
return ErrorResponseInvalidRequest(id); | ||
} | ||
else { | ||
let id = (data ).id; | ||
if (id === undefined$1) id = null; | ||
return ErrorResponseInvalidRequest(id) | ||
} else { | ||
// ? Ignore this message. The message channel maybe also used to transfer other message too. | ||
return undefined$1; | ||
return undefined$1 | ||
} | ||
} | ||
} catch (e) { | ||
if (log_localError) console_error(e, data, result); | ||
return ErrorResponseParseError(e, mapError || defaultErrorMapper(e && e.stack)) | ||
} | ||
catch (e) { | ||
if (log_localError) | ||
console_error(e, data, result); | ||
return ErrorResponseParseError(e, mapError || defaultErrorMapper(e && e.stack)); | ||
} | ||
}; | ||
const rawMessageSender = async (res) => { | ||
if (!res) | ||
return; | ||
if (!res) return | ||
if (isArray(res)) { | ||
const reply = res.filter((x) => x && hasKey(x, 'id')); | ||
if (reply.length === 0) | ||
return; | ||
return serialization(reply); | ||
if (reply.length === 0) return | ||
return serialization(reply) | ||
} else { | ||
return serialization(res) | ||
} | ||
else { | ||
return serialization(res); | ||
} | ||
}; | ||
@@ -543,17 +648,23 @@ const serialization = (x) => serializer.serialization(x); | ||
const isEventBasedChannel = (x) => hasKey(x, 'send') && isFunction(x.send); | ||
const isCallbackBasedChannel = (x) => hasKey(x, 'setup') && isFunction(x.setup); | ||
const isCallbackBasedChannel = (x) => | ||
hasKey(x, 'setup') && isFunction(x.setup); | ||
if (isCallbackBasedChannel(channel)) { | ||
channel.setup((data) => rawMessageReceiver(data).then(rawMessageSender), (data) => { | ||
const _ = deserialization(data); | ||
if (isJSONRPCObject(_)) | ||
return true; | ||
return Promise_resolve(_).then(isJSONRPCObject); | ||
}); | ||
channel.setup( | ||
(data) => rawMessageReceiver(data).then(rawMessageSender), | ||
(data) => { | ||
const _ = deserialization(data); | ||
if (isJSONRPCObject(_)) return true | ||
return Promise_resolve(_).then(isJSONRPCObject) | ||
}, | ||
); | ||
} | ||
if (isEventBasedChannel(channel)) { | ||
const m = channel; | ||
const m = channel; | ||
m.on && | ||
m.on((_) => rawMessageReceiver(_) | ||
.then(rawMessageSender) | ||
.then((x) => x && m.send(x))); | ||
m.on((_) => | ||
rawMessageReceiver(_) | ||
.then(rawMessageSender) | ||
.then((x) => x && m.send(x)), | ||
); | ||
} | ||
@@ -565,11 +676,10 @@ function makeErrorObject(e, frameworkStack, data) { | ||
.reduce((stack, fstack) => stack.replace(fstack + '\n', ''), '' + e.stack); | ||
if (log_localError) | ||
console_error(e); | ||
return ErrorResponseMapped(data, e, mapError || defaultErrorMapper(log_sendLocalStack ? e.stack : undefined$1)); | ||
if (log_localError) console_error(e); | ||
return ErrorResponseMapped(data, e, mapError || defaultErrorMapper(log_sendLocalStack ? e.stack : undefined$1)) | ||
} | ||
async function sendPayload(payload, removeQueueR = false) { | ||
if (removeQueueR) | ||
payload = [...payload]; | ||
if (removeQueueR) payload = [...(payload )]; | ||
const data = await serialization(payload); | ||
return channel.send(data); | ||
return channel.send(data) | ||
} | ||
@@ -584,26 +694,29 @@ function rejectsQueue(queue, error) { | ||
} | ||
const handleSingleMessage = async (data) => { | ||
const handleSingleMessage = async ( | ||
data, | ||
) => { | ||
if (hasKey(data, 'method')) { | ||
const r = onRequest(data); | ||
if (hasKey(data, 'id')) | ||
return r; | ||
if (hasKey(data, 'id')) return r | ||
try { | ||
await r; | ||
} | ||
catch (_a) { } | ||
return undefined$1; // Does not care about return result for notifications | ||
} catch (e2) {} | ||
return undefined$1 // Does not care about return result for notifications | ||
} | ||
return onResponse(data); | ||
return onResponse(data) | ||
}; | ||
return new Proxy({ __proto__: null }, { | ||
return new Proxy({ __proto__: null } , { | ||
get(cache, method) { | ||
if (method === 'then') { | ||
if (thenable === undefined$1) { | ||
console_warn(makeHostedMessage(3 /* Instance_is_treated_as_Promise_please_explicitly_mark_if_it_is_thenable_or_not_via_the_options */, new TypeError('RPC used as Promise: '))); | ||
console_warn( | ||
makeHostedMessage( | ||
Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options, | ||
new TypeError('RPC used as Promise: '), | ||
), | ||
); | ||
} | ||
if (thenable !== true) | ||
return undefined$1; | ||
if (thenable !== true) return undefined$1 | ||
} | ||
if (isString(method) && cache[method]) | ||
return cache[method]; | ||
if (isString(method) && cache[method]) return cache[method] | ||
const factory = (notify) => (...params) => { | ||
@@ -613,21 +726,20 @@ let stack = removeStackHeader(new Error().stack); | ||
if (method === AsyncCallBatch) { | ||
queue = params.shift(); | ||
method = params.shift(); | ||
queue = params.shift(); | ||
method = params.shift(); | ||
} | ||
if (typeof method === 'symbol') { | ||
const RPCInternalMethod = Symbol.keyFor(method) || method.description; | ||
const RPCInternalMethod = Symbol.keyFor(method) || (method ).description; | ||
if (RPCInternalMethod) { | ||
if (RPCInternalMethod.startsWith('rpc.')) | ||
method = RPCInternalMethod; | ||
else | ||
return Promise_reject(new TypeError('Not start with rpc.')); | ||
if (RPCInternalMethod.startsWith('rpc.')) method = RPCInternalMethod; | ||
else return Promise_reject(new TypeError('Not start with rpc.')) | ||
} | ||
} | ||
else if (method.startsWith('rpc.')) | ||
return Promise_reject(makeHostedMessage(2 /* Can_not_call_method_starts_with_rpc_dot_directly */, new TypeError())); | ||
} else if (method.startsWith('rpc.')) | ||
return Promise_reject( | ||
makeHostedMessage(Err_Cannot_call_method_starts_with_rpc_dot_directly, new TypeError()), | ||
) | ||
return new Promise((resolve, reject) => { | ||
if (preferLocalImplementation && !isThisSideImplementationPending && isString(method)) { | ||
const localImpl = resolvedThisSideImplementationValue && resolvedThisSideImplementationValue[method]; | ||
if (isFunction(localImpl)) | ||
return resolve(localImpl(...params)); | ||
const localImpl = | ||
resolvedThisSideImplementationValue && (resolvedThisSideImplementationValue )[method]; | ||
if (isFunction(localImpl)) return resolve(localImpl(...params)) | ||
} | ||
@@ -637,13 +749,10 @@ const id = idGenerator(); | ||
const sendingStack = log_sendLocalStack ? stack : ''; | ||
const param = parameterStructures === 'by-name' && params.length === 1 && isObject(param0) ? param0 : params; | ||
const request = Request(notify ? undefined$1 : id, method, param, sendingStack); | ||
const param = | ||
parameterStructures === 'by-name' && params.length === 1 && isObject(param0) ? param0 : params; | ||
const request = Request(notify ? undefined$1 : id, method , param, sendingStack); | ||
if (queue) { | ||
queue.push(request); | ||
if (!queue.r) | ||
queue.r = [() => sendPayload(queue, true), (e) => rejectsQueue(queue, e)]; | ||
} | ||
else | ||
sendPayload(request).catch(reject); | ||
if (notify) | ||
return resolve(); | ||
if (!queue.r) queue.r = [() => sendPayload(queue, true), (e) => rejectsQueue(queue, e)]; | ||
} else sendPayload(request).catch(reject); | ||
if (notify) return resolve() | ||
requestContext.set(id, { | ||
@@ -653,3 +762,3 @@ f: [resolve, reject], | ||
}); | ||
}); | ||
}) | ||
}; | ||
@@ -662,16 +771,66 @@ const f = factory(false); | ||
isString(method) && Object.defineProperty(cache, method, { value: f, configurable: true }); | ||
return f; | ||
return f | ||
}, | ||
}); | ||
}) | ||
} | ||
// Assume a console object in global if there is no custom logger provided | ||
/** | ||
function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }/** | ||
* See the document at https://github.com/Jack-Works/async-call/ | ||
*/ | ||
const i$1 = 'rpc.async-iterator.'; | ||
const i = 'rpc.async-iterator.'; | ||
// ! side effect | ||
const AsyncIteratorStart = Symbol.for(i$1 + 'start'); | ||
const AsyncIteratorNext = Symbol.for(i$1 + 'next'); | ||
const AsyncIteratorReturn = Symbol.for(i$1 + 'return'); | ||
const AsyncIteratorThrow = Symbol.for(i$1 + 'throw'); | ||
const AsyncIteratorStart = Symbol.for(i + 'start'); | ||
const AsyncIteratorNext = Symbol.for(i + 'next'); | ||
const AsyncIteratorReturn = Symbol.for(i + 'return'); | ||
const AsyncIteratorThrow = Symbol.for(i + 'throw'); | ||
/** | ||
@@ -713,27 +872,29 @@ * The async generator version of the AsyncCall | ||
*/ | ||
function AsyncGeneratorCall(thisSideImplementation, options) { | ||
var _a; | ||
function AsyncGeneratorCall( | ||
thisSideImplementation, | ||
options, | ||
) { | ||
const iterators = new Map(); | ||
const [methodNotFound] = normalizeStrictOptions((_a = options.strict) !== null && _a !== void 0 ? _a : true); | ||
const [methodNotFound] = normalizeStrictOptions(_nullishCoalesce(options.strict, () => ( true))); | ||
const { idGenerator = generateRandomID } = options; | ||
const findIterator = (id, next) => { | ||
const findIterator = ( | ||
id, | ||
next, | ||
) => { | ||
const it = iterators.get(id); | ||
if (!it) { | ||
if (methodNotFound) | ||
throw makeHostedMessage(0 /* AsyncCallGenerator_cannot_find_a_running_iterator_with_the_given_ID */, new Error(`Iterator ${id}, `)); | ||
else | ||
return AsyncCallIgnoreResponse; | ||
throw makeHostedMessage(Err_Cannot_find_a_running_iterator_with_given_ID, new Error(`Iterator ${id}, `)) | ||
else return AsyncCallIgnoreResponse | ||
} | ||
const result = next(it); | ||
isFinished(result, () => iterators.delete(id)); | ||
return result; | ||
return result | ||
}; | ||
const server = { | ||
async [AsyncIteratorStart](method, args) { | ||
const iteratorGenerator = (await thisSideImplementation)[method]; | ||
const iteratorGenerator = ((await thisSideImplementation) )[method]; | ||
if (!isFunction(iteratorGenerator)) { | ||
if (methodNotFound) | ||
throw new TypeError(method + ' is not a function'); | ||
else | ||
return AsyncCallIgnoreResponse; | ||
if (methodNotFound) throw new TypeError(method + ' is not a function') | ||
else return AsyncCallIgnoreResponse | ||
} | ||
@@ -743,30 +904,36 @@ const iterator = iteratorGenerator(...args); | ||
iterators.set(id, iterator); | ||
return Promise_resolve(id); | ||
return Promise_resolve(id) | ||
}, | ||
[AsyncIteratorNext](id, val) { | ||
return findIterator(id, (it) => it.next(val)); | ||
return findIterator(id, (it) => it.next(val )) | ||
}, | ||
[AsyncIteratorReturn](id, val) { | ||
return findIterator(id, (it) => isFunction(it.return) && it.return(val)); | ||
return findIterator(id, (it) => isFunction(it.return) && it.return(val)) | ||
}, | ||
[AsyncIteratorThrow](id, val) { | ||
return findIterator(id, (it) => isFunction(it.throw) && it.throw(val)); | ||
return findIterator(id, (it) => isFunction(it.throw) && it.throw(val)) | ||
}, | ||
}; | ||
}; | ||
const remote = AsyncCall(server, options); | ||
const proxyTrap = (cache, key) => { | ||
if (!isString(key)) | ||
throw makeHostedMessage(1 /* Only_string_can_be_the_RPC_method_name */, new TypeError('')); | ||
if (cache[key]) | ||
return cache[key]; | ||
throw makeHostedMessage(Err_Only_string_can_be_the_RPC_method_name, new TypeError('')) | ||
if (cache[key]) return cache[key] | ||
const f = (...args) => { | ||
const id = remote[AsyncIteratorStart](key, args); | ||
return new _AsyncGenerator(remote, id); | ||
return new _AsyncGenerator(remote, id) | ||
}; | ||
Object.defineProperty(cache, key, { value: f, configurable: true }); | ||
return f; | ||
return f | ||
}; | ||
return new Proxy({ __proto__: null }, { get: proxyTrap }); | ||
return new Proxy({ __proto__: null }, { get: proxyTrap }) | ||
} | ||
class _AsyncGenerator { | ||
class _AsyncGenerator { | ||
/** done? */ | ||
__init() {this.d = false;} | ||
/** check */ | ||
__init2() {this.c = async (val) => { | ||
await isFinished(val, () => (this.d = true)); | ||
return val | ||
};} | ||
/** | ||
@@ -776,31 +943,20 @@ * @param r Remote Implementation | ||
*/ | ||
constructor(r, i) { | ||
this.r = r; | ||
this.i = i; | ||
/** done? */ | ||
this.d = false; | ||
/** check */ | ||
this.c = async (val) => { | ||
await isFinished(val, () => (this.d = true)); | ||
return val; | ||
}; | ||
} | ||
constructor( r, i) {this.r = r;this.i = i;_AsyncGenerator.prototype.__init.call(this);_AsyncGenerator.prototype.__init2.call(this);} | ||
async return(val) { | ||
if (this.d) | ||
return makeIteratorResult(true, val); | ||
return this.c(this.r[AsyncIteratorReturn](await this.i, val)); | ||
if (this.d) return makeIteratorResult(true, val) | ||
return this.c(this.r[AsyncIteratorReturn](await this.i, val)) | ||
} | ||
async next(val) { | ||
if (this.d) | ||
return makeIteratorResult(true); | ||
return this.c(this.r[AsyncIteratorNext](await this.i, val)); | ||
if (this.d) return makeIteratorResult(true) | ||
return this.c(this.r[AsyncIteratorNext](await this.i, val)) | ||
} | ||
async throw(val) { | ||
if (!this.d) | ||
return this.c(this.r[AsyncIteratorThrow](await this.i, val)); | ||
throw val; | ||
if (!this.d) return this.c(this.r[AsyncIteratorThrow](await this.i, val)) | ||
throw val | ||
} | ||
// Inherited from AsyncGeneratorPrototype | ||
} | ||
// ! side effect | ||
const EmptyAsyncGenerator = async function* () { }; | ||
const EmptyAsyncGenerator = async function* () {}; | ||
const AsyncGeneratorConstructor = EmptyAsyncGenerator.constructor; | ||
@@ -811,2 +967,3 @@ const AsyncGeneratorConstructorPrototype = AsyncGeneratorConstructor.prototype; | ||
Object_setPrototypeOf(_AsyncGenerator.prototype, AsyncGeneratorPrototype); | ||
const isFinished = async (result, cb) => { | ||
@@ -816,5 +973,5 @@ try { | ||
x && x.done && cb(); | ||
} | ||
catch (_a) { } | ||
} catch (e) {} | ||
}; | ||
const makeIteratorResult = (done, value = undefined) => ({ | ||
@@ -821,0 +978,0 @@ done, |
/** | ||
* ! This file MUST NOT contain any import statement. | ||
* ! This file is part of public API of this package (for Deno users). | ||
*/ | ||
/** | ||
* This interface represents a "on message" - "send response" model. | ||
@@ -37,103 +41,3 @@ * @remarks | ||
} | ||
/** | ||
* Serialize and deserialize of the JSON RPC payload | ||
* @public | ||
*/ | ||
interface Serialization { | ||
/** | ||
* Serialize data | ||
* @param from - original data | ||
*/ | ||
serialization(from: any): unknown | PromiseLike<unknown>; | ||
/** | ||
* Deserialize data | ||
* @param serialized - Serialized data | ||
*/ | ||
deserialization(serialized: unknown): unknown | PromiseLike<unknown>; | ||
} | ||
/** | ||
* Serialization implementation that do nothing | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const NoSerialization: Serialization; | ||
/** | ||
* Create a serialization by JSON.parse/stringify | ||
* | ||
* @param replacerAndReceiver - Replacer and receiver of JSON.parse/stringify | ||
* @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. | ||
* @param undefinedKeepingBehavior - How to keep "undefined" in result of SuccessResponse? | ||
* | ||
* If it is not handled properly, JSON.stringify will emit an invalid JSON RPC object. | ||
* | ||
* Options: | ||
* - `"null"`(**default**): convert it to null. | ||
* - `"keep"`: try to keep it by additional property "undef". | ||
* - `false`: Don't keep it, let it break. | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const JSONSerialization: (replacerAndReceiver?: [(((key: string, value: any) => any) | undefined)?, (((key: string, value: any) => any) | undefined)?], space?: string | number | undefined, undefinedKeepingBehavior?: 'keep' | 'null' | false) => Serialization; | ||
/** | ||
* The minimal Console interface that AsyncCall needs. | ||
* @public | ||
* @remarks | ||
* The method not provided will use "log" as it's fallback. | ||
*/ | ||
interface Console { | ||
warn?(...args: unknown[]): void; | ||
debug?(...args: unknown[]): void; | ||
log(...args: unknown[]): void; | ||
groupCollapsed?(...args: unknown[]): void; | ||
groupEnd?(...args: unknown[]): void; | ||
error?(...args: unknown[]): void; | ||
} | ||
/** | ||
* Make the returning type to `Promise<void>` | ||
* @internal | ||
* @remarks | ||
* Due to the limitation of TypeScript, generic signatures cannot be preserved | ||
* if the function is the top level parameter of this utility type, | ||
* or the function is not returning `Promise<void>`. | ||
*/ | ||
declare type _IgnoreResponse<T> = T extends (...args: infer Args) => unknown ? (...args: Args) => Promise<void> : { | ||
[key in keyof T as T[key] extends Function ? key : never]: T[key] extends (...args: infer Args) => infer Return ? Return extends Promise<void> ? T[key] : (...args: Args) => Promise<void> : never; | ||
}; | ||
/** | ||
* Wrap the AsyncCall instance to send notification. | ||
* @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance | ||
* @example | ||
* const notifyOnly = notify(AsyncCall(...)) | ||
* @public | ||
*/ | ||
declare function notify<T extends object>(instanceOrFnOnInstance: T): _IgnoreResponse<T>; | ||
/** | ||
* Wrap the AsyncCall instance to use batch call. | ||
* @param asyncCallInstance - The AsyncCall instance | ||
* @example | ||
* const [batched, send, drop] = batch(AsyncCall(...)) | ||
* batched.call1() // pending | ||
* batched.call2() // pending | ||
* send() // send all pending requests | ||
* drop() // drop all pending requests | ||
* @returns It will return a tuple. | ||
* | ||
* The first item is the batched version of AsyncCall instance passed in. | ||
* | ||
* The second item is a function to send all pending requests. | ||
* | ||
* The third item is a function to drop and reject all pending requests. | ||
* @public | ||
*/ | ||
declare function batch<T extends object>(asyncCallInstance: T): [T, () => void, (error?: unknown) => void]; | ||
/** | ||
* See the document at https://github.com/Jack-Works/async-call/ | ||
*/ | ||
/** | ||
* Log options of AsyncCall | ||
@@ -222,3 +126,3 @@ * @remarks | ||
* | ||
* - {@link https://github.com/jack-works/async-call-rpc#web-deno-and-node-bson BSONSerialization} (use the {@link https://npmjs.org/bson | bson} as the serializer) | ||
* - {@link https://github.com/jack-works/async-call-rpc#web-deno-and-node-bson | BSONSerialization} (use the {@link https://npmjs.org/bson | bson} as the serializer) | ||
* | ||
@@ -231,6 +135,6 @@ * @defaultValue {@link NoSerialization} | ||
* @remarks | ||
* See {@link Console} | ||
* See {@link ConsoleInterface} | ||
* @defaultValue globalThis.console | ||
*/ | ||
logger?: Console; | ||
logger?: ConsoleInterface; | ||
/** | ||
@@ -349,2 +253,98 @@ * The message channel to exchange messages between server and client | ||
/** | ||
* The minimal Console interface that AsyncCall needs. | ||
* @public | ||
* @remarks | ||
* The method not provided will use "log" as it's fallback. | ||
*/ | ||
interface ConsoleInterface { | ||
warn?(...args: unknown[]): void; | ||
debug?(...args: unknown[]): void; | ||
log(...args: unknown[]): void; | ||
groupCollapsed?(...args: unknown[]): void; | ||
groupEnd?(...args: unknown[]): void; | ||
error?(...args: unknown[]): void; | ||
} | ||
/** | ||
* Serialize and deserialize of the JSON RPC payload | ||
* @public | ||
*/ | ||
interface Serialization { | ||
/** | ||
* Serialize data | ||
* @param from - original data | ||
*/ | ||
serialization(from: any): unknown | PromiseLike<unknown>; | ||
/** | ||
* Deserialize data | ||
* @param serialized - Serialized data | ||
*/ | ||
deserialization(serialized: unknown): unknown | PromiseLike<unknown>; | ||
} | ||
/** | ||
* Make the returning type to `Promise<void>` | ||
* @internal | ||
* @remarks | ||
* Due to the limitation of TypeScript, generic signatures cannot be preserved | ||
* if the function is the top level parameter of this utility type, | ||
* or the function is not returning `Promise<void>`. | ||
*/ | ||
declare type _IgnoreResponse<T> = T extends (...args: infer Args) => unknown ? (...args: Args) => Promise<void> : { | ||
[key in keyof T as T[key] extends Function ? key : never]: T[key] extends (...args: infer Args) => infer Return ? Return extends Promise<void> ? T[key] : (...args: Args) => Promise<void> : never; | ||
}; | ||
/** | ||
* Wrap the AsyncCall instance to send notification. | ||
* @param instanceOrFnOnInstance - The AsyncCall instance or function on the AsyncCall instance | ||
* @example | ||
* const notifyOnly = notify(AsyncCall(...)) | ||
* @public | ||
*/ | ||
declare function notify<T extends object>(instanceOrFnOnInstance: T): _IgnoreResponse<T>; | ||
/** | ||
* Serialization implementation that do nothing | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const NoSerialization: Serialization; | ||
/** | ||
* Create a serialization by JSON.parse/stringify | ||
* | ||
* @param replacerAndReceiver - Replacer and receiver of JSON.parse/stringify | ||
* @param space - Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. | ||
* @param undefinedKeepingBehavior - How to keep "undefined" in result of SuccessResponse? | ||
* | ||
* If it is not handled properly, JSON.stringify will emit an invalid JSON RPC object. | ||
* | ||
* Options: | ||
* - `"null"`(**default**): convert it to null. | ||
* - `"keep"`: try to keep it by additional property "undef". | ||
* - `false`: Don't keep it, let it break. | ||
* @remarks {@link Serialization} | ||
* @public | ||
*/ | ||
declare const JSONSerialization: (replacerAndReceiver?: [(((key: string, value: any) => any) | undefined)?, (((key: string, value: any) => any) | undefined)?], space?: string | number | undefined, undefinedKeepingBehavior?: 'keep' | 'null' | false) => Serialization; | ||
/** | ||
* Wrap the AsyncCall instance to use batch call. | ||
* @param asyncCallInstance - The AsyncCall instance | ||
* @example | ||
* const [batched, send, drop] = batch(AsyncCall(...)) | ||
* batched.call1() // pending | ||
* batched.call2() // pending | ||
* send() // send all pending requests | ||
* drop() // drop all pending requests | ||
* @returns It will return a tuple. | ||
* | ||
* The first item is the batched version of AsyncCall instance passed in. | ||
* | ||
* The second item is a function to send all pending requests. | ||
* | ||
* The third item is a function to drop and reject all pending requests. | ||
* @public | ||
*/ | ||
declare function batch<T extends object>(asyncCallInstance: T): [T, () => void, (error?: unknown) => void]; | ||
/** | ||
* Create a RPC server & client. | ||
@@ -427,2 +427,2 @@ * | ||
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, AsyncGeneratorCall, CallbackBasedChannel, Console, ErrorMapFunction, EventBasedChannel, JSONRPCRequest, JSONSerialization, NoSerialization, Serialization, _AsyncGeneratorVersionOf, _AsyncVersionOf, _IgnoreResponse, _IteratorLikeToAsyncGenerator, _IteratorOrIterableFunction, batch, notify }; | ||
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, AsyncGeneratorCall, CallbackBasedChannel, ConsoleInterface as Console, ConsoleInterface, ErrorMapFunction, EventBasedChannel, JSONRPCRequest, JSONSerialization, NoSerialization, Serialization, _AsyncGeneratorVersionOf, _AsyncVersionOf, _IgnoreResponse, _IteratorLikeToAsyncGenerator, _IteratorOrIterableFunction, batch, notify }; |
/// <reference types="./full.min.d.ts" /> | ||
((r,t)=>{"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((r="undefined"!=typeof globalThis?globalThis:r||self).AsyncCall={})})(this,(function(r){"use strict";class t extends Error{constructor(r,t,e,n){super(t),this.name=r,this.code=e,this.stack=n}}function e(r,t){return t.message+=`Error ${r}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#`+r,t}const n={__proto__:null,Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},o="DOMException:",s=r=>(r+"").replace(/^.+\n.+\n/,""),i=()=>{try{return DOMException}catch(r){}},a=r=>"string"==typeof r,c=r=>"boolean"==typeof r,l=r=>"function"==typeof r,u=r=>"object"==typeof r&&null!==r,f="Error",d=void 0,y=Object.setPrototypeOf,p=r=>Promise.reject(r),h=r=>Promise.resolve(r),m=Array.isArray,g="2.0",w=(r,t,e,n)=>{r===d&&(r=null),Number.isNaN(t=Math.floor(t))&&(t=-1);const o={jsonrpc:g,id:r,error:{code:t,message:e,data:n}};return P(o.error,"data"),o},b=(r,t,e)=>{const{id:n}=r,{code:o,message:s,data:i}=e(t,r);return w(n,o,s,i)},E=(r="",t=-1)=>e=>{let n=S("",(()=>e.message)),s=S(f,((r=e.constructor)=>l(r)&&r.name));const u=i();return u&&e instanceof u&&(s=o+e.name),(a(e)||"number"==typeof e||c(e)||"bigint"==typeof e)&&(s=f,n=e+""),{code:t,message:n,data:r?{stack:r,type:s}:{type:s}}},k=r=>{if(!u(r))return!1;if(!$(r,"jsonrpc"))return!1;if(r.jsonrpc!==g)return!1;if($(r,"params")){const t=r.params;if(!m(t)&&!u(t))return!1}return!0},$=(r,t)=>t in r,S=(r,t)=>{try{const e=t();return e===d?r:e+""}catch(t){return r}},P=(r,t)=>{r[t]===d&&delete r[t]},v={serialization:r=>r,deserialization:r=>r},_="AsyncCall/",j=Symbol.for(_+"ignored"),x=Symbol.for(_+"notify"),O=Symbol.for(_+"batch"),M=(r,t)=>r[t][x],z=()=>Math.random().toString(36).slice(2),A=r=>void 0===r||r,C=r=>{if(!c(r)){const{methodNotFound:t,unknownMessage:e}=r;return[t,e]}return[r,r]};function N(r,y){let S=!0,_=d,M=d;const N=async()=>{try{_=await r}catch(r){M=r,tr("AsyncCall failed to start",r)}finally{S=!1}},{serializer:R=v,key:T="rpc",strict:I=!0,log:W=!0,parameterStructures:J="by-position",preferLocalImplementation:G=!1,idGenerator:q=z,mapError:D,logger:F,channel:L,thenable:U}=y;r instanceof Promise?N():(_=r,S=!1);const[B,H]=C(I),[K,Q,V,X,Y,Z]=(r=>{if("all"===r)return[!0,!0,!0,!0,!0,!0];if(!c(r)){const{beCalled:t,localError:e,remoteError:n,type:o,requestReplay:s,sendLocalStack:i}=r;return[A(t),A(e),A(n),"basic"!==o,s,i]}return r?[!0,!0,!0,!0]:[]})(W),{log:rr,error:tr=rr,debug:er=rr,groupCollapsed:nr=rr,groupEnd:or=rr,warn:sr=rr}=F||console,ir=new Map,ar=async r=>{let t;try{if(t=await ur(r),k(t))return await pr(t);if(m(t)&&t.every(k)&&0!==t.length)return Promise.all(t.map(pr));if(H){let r=t.id;return r===d&&(r=null),(r=>w(r,-32600,"Invalid Request"))(r)}return d}catch(r){return Q&&tr(r,t,void 0),((r,t)=>{const e=b({},r,t),n=e.error;return n.code=-32700,n.message="Parse error",e})(r,D||E(r&&r.stack))}},cr=async r=>{if(r){if(m(r)){const t=r.filter((r=>r&&$(r,"id")));if(0===t.length)return;return lr(t)}return lr(r)}},lr=r=>R.serialization(r),ur=r=>R.deserialization(r);var fr;if($(fr=L,"setup")&&l(fr.setup)&&L.setup((r=>ar(r).then(cr)),(r=>{const t=ur(r);return!!k(t)||h(t).then(k)})),(r=>$(r,"send")&&l(r.send))(L)){const r=L;r.on&&r.on((t=>ar(t).then(cr).then((t=>t&&r.send(t)))))}function dr(r,t,e){return u(r)&&$(r,"stack")&&(r.stack=t.split("\n").reduce(((r,t)=>r.replace(t+"\n","")),""+r.stack)),Q&&tr(r),b(e,r,D||E(Z?r.stack:d))}async function yr(r,t=!1){t&&(r=[...r]);const e=await lr(r);return L.send(e)}const pr=async r=>{if($(r,"method")){const t=(async r=>{if(S)await N();else if(M)return dr(M,"",r);let t="";try{const{params:e,method:n,id:o,remoteStack:i}=r,a=n.startsWith("rpc.")?Symbol.for(n):n,c=_&&_[a];if(!l(c))return B?w(o,-32601,"Method not found"):void(Q&&er("Missing method",a,r));const u=m(e)?e:[e];t=s(Error().stack);const f=new Promise((r=>r(c.apply(_,u))));if(K)if(X){const r=[`${T}.%c${n}%c(${u.map((()=>"%o")).join(", ")}%c)\n%o %c@${o}`,"color: #d2c057","",...u,"",f,"color: gray; font-style: italic;"];Y&&r.push((()=>{debugger;return c.apply(_,u)})),i?(nr(...r),rr(i),or()):rr(...r)}else rr(`${T}.${n}(${""+[...u]}) @${o}`);if(await f===j)return;return((r,t)=>{const e={jsonrpc:g,id:r,result:t};return P(e,"id"),e})(o,await f)}catch(e){return dr(e,t,r)}})(r);if($(r,"id"))return t;try{await t}catch(r){}return d}return(async r=>{let e="",s="",c=0,l=f;if($(r,"error")){const t=r.error;e=t.message,c=t.code;const n=t.data;s=u(n)&&$(n,"stack")&&a(n.stack)?n.stack:"<remote stack not available>",l=u(n)&&$(n,"type")&&a(n.type)?n.type:f,V&&(X?tr(`${l}: ${e}(${c}) %c@${r.id}\n%c${s}`,"color: gray",""):tr(`${l}: ${e}(${c}) @${r.id}\n${s}`))}if(null===r.id||r.id===d)return;const{f:[y,p]=[null,null],stack:h=""}=ir.get(r.id)||{};y&&p&&(ir.delete(r.id),$(r,"error")?p(((r,e,s,a)=>{try{const c=i();if(r.startsWith(o)&&c)return new c(e,r.slice(13));if(r in n){const t=new n[r](e);return t.stack=a,t.code=s,t}return new t(r,e,s,a)}catch(t){return Error(`E${s} ${r}: ${e}\n${a}`)}})(l,e,c,s+"\n аt AsyncCall (rpc) \n"+h)):y(r.result))})(r)};return new Proxy({__proto__:null},{get(r,t){if("then"===t&&(U===d&&sr(e(3,new TypeError("RPC used as Promise: "))),!0!==U))return d;if(a(t)&&r[t])return r[t];const n=r=>(...n)=>{let o=s(Error().stack),i=d;if(t===O&&(i=n.shift(),t=n.shift()),"symbol"==typeof t){const r=Symbol.keyFor(t)||t.description;if(r){if(!r.startsWith("rpc."))return p(new TypeError("Not start with rpc."));t=r}}else if(t.startsWith("rpc."))return p(e(2,new TypeError));return new Promise(((e,s)=>{if(G&&!S&&a(t)){const r=_&&_[t];if(l(r))return e(r(...n))}const c=q(),[f]=n,y=Z?o:"",p="by-name"===J&&1===n.length&&u(f)?f:n,h=((r,t,e,n)=>{const o={jsonrpc:g,id:r,method:t,params:e,remoteStack:n};return P(o,"id"),((r,t)=>{r[t]||delete r[t]})(o,"remoteStack"),o})(r?d:c,t,p,y);if(i?(i.push(h),i.r||(i.r=[()=>yr(i,!0),r=>((r,t)=>{for(const e of r)if($(e,"id")){const r=ir.get(e.id);r&&r.f[1](t)}})(i,r)])):yr(h).catch(s),r)return e();ir.set(c,{f:[e,s],stack:o})}))},o=n(!1);return o[x]=n(!0),o[x][x]=o[x],a(t)&&Object.defineProperty(r,t,{value:o,configurable:!0}),o}})}const R="rpc.async-iterator.",T=Symbol.for(R+"start"),I=Symbol.for(R+"next"),W=Symbol.for(R+"return"),J=Symbol.for(R+"throw");class G{constructor(r,t){this.r=r,this.i=t,this.d=!1,this.c=async r=>(await F(r,(()=>this.d=!0)),r)}async return(r){return this.d?L(!0,r):this.c(this.r[W](await this.i,r))}async next(r){return this.d?L(!0):this.c(this.r[I](await this.i,r))}async throw(r){if(!this.d)return this.c(this.r[J](await this.i,r));throw r}}const q=async function*(){};y(G,q.constructor.prototype);const D=Object.getPrototypeOf(q());y(G.prototype,D);const F=async(r,t)=>{try{const e=await r;e&&e.done&&t()}catch(r){}},L=(r,t)=>({done:r,value:t});r.AsyncCall=N,r.AsyncGeneratorCall=(r,t)=>{var n;const o=new Map,[s]=C(null===(n=t.strict)||void 0===n||n),{idGenerator:i=z}=t,c=(r,t)=>{const n=o.get(r);if(!n){if(s)throw e(0,Error(`Iterator ${r}, `));return j}const i=t(n);return F(i,(()=>o.delete(r))),i},u=N({async[T](t,e){const n=(await r)[t];if(!l(n)){if(s)throw new TypeError(t+" is not a function");return j}const a=n(...e),c=i();return o.set(c,a),h(c)},[I]:(r,t)=>c(r,(r=>r.next(t))),[W]:(r,t)=>c(r,(r=>l(r.return)&&r.return(t))),[J]:(r,t)=>c(r,(r=>l(r.throw)&&r.throw(t)))},t);return new Proxy({__proto__:null},{get:(r,t)=>{if(!a(t))throw e(1,new TypeError(""));if(r[t])return r[t];const n=(...r)=>{const e=u[T](t,r);return new G(u,e)};return Object.defineProperty(r,t,{value:n,configurable:!0}),n}})},r.JSONSerialization=(r=[d,d],t,e="null")=>({serialization(n){if(e&&u(n)&&$(n,"result")&&n.result===d){const r={...n};r.result=null,"keep"===e&&(r.undef=!0),n=r}return JSON.stringify(n,r[0],t)},deserialization(t){const e=JSON.parse(t,r[1]);return u(e)&&$(e,"result")&&null===e.result&&$(e,"undef")&&!0===e.undef&&(e.result=d,delete e.undef),e}}),r.NoSerialization=v,r.batch=r=>{let t=[];return[new Proxy({__proto__:null},{get(e,n){if(a(n)&&e[n])return e[n];const o=(...e)=>r[O](t,n,...e);return(o[x]=(...e)=>r[O][x](t,n,...e))[x]=o[x],a(n)&&Object.defineProperty(e,n,{value:o,configurable:!0}),o}}),(r=t.r)=>r&&r[0](),(r=Error("Aborted"),e=t.r)=>{e&&e[1](r),t=[]}]},r.notify=r=>l(r)?r[x]:new Proxy(r,{get:M}),Object.defineProperty(r,"__esModule",{value:!0})})); | ||
((r,t)=>{"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((r="undefined"!=typeof globalThis?globalThis:r||self).AsyncCall={})})(this,(function(r){"use strict";class t extends Error{constructor(r,t,e,n){super(t),this.name=r,this.code=e,this.stack=n}}const e={},n={},o={},s={},i=[e,n,o,s];function a(r,t){const e=i.indexOf(r);return t.message+=`Error ${e}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#`+e,t}const c={__proto__:null,Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},l="DOMException:",u=r=>(r+"").replace(/^.+\n.+\n/,""),f=()=>{try{return DOMException}catch(r){}},d=r=>"string"==typeof r,y=r=>"boolean"==typeof r,p=r=>"function"==typeof r,h=r=>"object"==typeof r&&null!==r,m="Error",g=void 0,w=Object.setPrototypeOf,b=r=>Promise.reject(r),E=r=>Promise.resolve(r),k=Array.isArray,_=()=>"() => replay()",$="2.0",S=(r,t,e,n)=>{r===g&&(r=null),Number.isNaN(t=Math.floor(t))&&(t=-1);const o={jsonrpc:$,id:r,error:{code:t,message:e,data:n}};return M(o.error,"data"),o},P=(r,t,e)=>{const{id:n}=r,{code:o,message:s,data:i}=e(t,r);return S(n,o,s,i)},v=(r="",t=-1)=>e=>{let n=O("",(()=>e.message)),o=O(m,((r=e.constructor)=>p(r)&&r.name));const s=f();return s&&e instanceof s&&(o=l+e.name),(d(e)||"number"==typeof e||y(e)||"bigint"==typeof e)&&(o=m,n=e+""),{code:t,message:n,data:r?{stack:r,type:o}:{type:o}}},x=r=>{if(!h(r))return!1;if(!j(r,"jsonrpc"))return!1;if(r.jsonrpc!==$)return!1;if(j(r,"params")){const t=r.params;if(!k(t)&&!h(t))return!1}return!0},j=(r,t)=>t in r,O=(r,t)=>{try{const e=t();return e===g?r:e+""}catch(t){return r}},M=(r,t)=>{r[t]===g&&delete r[t]},z={serialization:r=>r,deserialization:r=>r},A="AsyncCall/",C=Symbol.for(A+"ignored"),N=Symbol.for(A+"notify"),R=Symbol.for(A+"batch"),T=(r,t)=>r[t][N],I=()=>Math.random().toString(36).slice(2),W=r=>void 0===r||r,J=r=>{if(!y(r)){const{methodNotFound:t,unknownMessage:e}=r;return[t,e]}return[r,r]};function G(r,e){let n=!0,i=g,w=g;const O=async()=>{try{i=await r}catch(r){w=r,or("AsyncCall failed to start",r)}finally{n=!1}},{serializer:A=z,key:T="rpc",strict:G=!0,log:q=!0,parameterStructures:D="by-position",preferLocalImplementation:F=!1,idGenerator:L=I,mapError:U,logger:B,channel:H,thenable:K}=e;r instanceof Promise?O():(i=r,n=!1);const[Q,V]=J(G),[X,Y,Z,rr,tr,er]=(r=>{if("all"===r)return[!0,!0,!0,!0,!0,!0];if(!y(r)){const{beCalled:t,localError:e,remoteError:n,type:o,requestReplay:s,sendLocalStack:i}=r;return[W(t),W(e),W(n),"basic"!==o,s,i]}return r?[!0,!0,!0,!0]:[]})(q),{log:nr,error:or=nr,debug:sr=nr,groupCollapsed:ir=nr,groupEnd:ar=nr,warn:cr=nr}=B||console,lr=new Map,ur=async r=>{let t;try{if(t=await yr(r),x(t))return await gr(t);if(k(t)&&t.every(x)&&0!==t.length)return Promise.all(t.map(gr));if(V){let r=t.id;return r===g&&(r=null),(r=>S(r,-32600,"Invalid Request"))(r)}return g}catch(r){return Y&&or(r,t,void 0),((r,t)=>{const e=P({},r,t),n=e.error;return n.code=-32700,n.message="Parse error",e})(r,U||v(r&&r.stack))}},fr=async r=>{if(r){if(k(r)){const t=r.filter((r=>r&&j(r,"id")));if(0===t.length)return;return dr(t)}return dr(r)}},dr=r=>A.serialization(r),yr=r=>A.deserialization(r);var pr;if(j(pr=H,"setup")&&p(pr.setup)&&H.setup((r=>ur(r).then(fr)),(r=>{const t=yr(r);return!!x(t)||E(t).then(x)})),(r=>j(r,"send")&&p(r.send))(H)){const r=H;r.on&&r.on((t=>ur(t).then(fr).then((t=>t&&r.send(t)))))}function hr(r,t,e){return h(r)&&j(r,"stack")&&(r.stack=t.split("\n").reduce(((r,t)=>r.replace(t+"\n","")),""+r.stack)),Y&&or(r),P(e,r,U||v(er?r.stack:g))}async function mr(r,t=!1){t&&(r=[...r]);const e=await dr(r);return H.send(e)}const gr=async r=>{if(j(r,"method")){const t=(async r=>{if(n)await O();else if(w)return hr(w,"",r);let t="";try{const{params:e,method:n,id:o,remoteStack:s}=r,a=n.startsWith("rpc.")?Symbol.for(n):n,c=i&&i[a];if(!p(c))return Q?S(o,-32601,"Method not found"):void(Y&&sr("Missing method",a,r));const l=k(e)?e:[e];t=u(Error().stack);const f=new Promise((r=>r(c.apply(i,l))));if(X)if(rr){const r=[`${T}.%c${n}%c(${l.map((()=>"%o")).join(", ")}%c)\n%o %c@${o}`,"color: #d2c057","",...l,"",f,"color: gray; font-style: italic;"];if(tr){const t=()=>{debugger;return c.apply(i,l)};t.toString=_,r.push(t)}s?(ir(...r),nr(s),ar()):nr(...r)}else nr(`${T}.${n}(${""+[...l]}) @${o}`);if(await f===C)return;return((r,t)=>{const e={jsonrpc:$,id:r,result:t};return M(e,"id"),e})(o,await f)}catch(e){return hr(e,t,r)}})(r);if(j(r,"id"))return t;try{await t}catch(r){}return g}return(async r=>{let e="",n="",o=0,s=m;if(j(r,"error")){const t=r.error;e=t.message,o=t.code;const i=t.data;n=h(i)&&j(i,"stack")&&d(i.stack)?i.stack:"<remote stack not available>",s=h(i)&&j(i,"type")&&d(i.type)?i.type:m,Z&&(rr?or(`${s}: ${e}(${o}) %c@${r.id}\n%c${n}`,"color: gray",""):or(`${s}: ${e}(${o}) @${r.id}\n${n}`))}if(null===r.id||r.id===g)return;const{f:[i,a]=[null,null],stack:u=""}=lr.get(r.id)||{};i&&a&&(lr.delete(r.id),j(r,"error")?a(((r,e,n,o)=>{try{const s=f();if(r.startsWith(l)&&s)return new s(e,r.slice(13));if(r in c){const t=new c[r](e);return t.stack=o,t.code=n,t}return new t(r,e,n,o)}catch(t){return Error(`E${n} ${r}: ${e}\n${o}`)}})(s,e,o,n+"\n аt AsyncCall (rpc) \n"+u)):i(r.result))})(r)};return new Proxy({__proto__:null},{get(r,t){if("then"===t&&(K===g&&cr(a(s,new TypeError("RPC used as Promise: "))),!0!==K))return g;if(d(t)&&r[t])return r[t];const e=r=>(...e)=>{let s=u(Error().stack),c=g;if(t===R&&(c=e.shift(),t=e.shift()),"symbol"==typeof t){const r=Symbol.keyFor(t)||t.description;if(r){if(!r.startsWith("rpc."))return b(new TypeError("Not start with rpc."));t=r}}else if(t.startsWith("rpc."))return b(a(o,new TypeError));return new Promise(((o,a)=>{if(F&&!n&&d(t)){const r=i&&i[t];if(p(r))return o(r(...e))}const l=L(),[u]=e,f=er?s:"",y="by-name"===D&&1===e.length&&h(u)?u:e,m=((r,t,e,n)=>{const o={jsonrpc:$,id:r,method:t,params:e,remoteStack:n};return M(o,"id"),((r,t)=>{r[t]||delete r[t]})(o,"remoteStack"),o})(r?g:l,t,y,f);if(c?(c.push(m),c.r||(c.r=[()=>mr(c,!0),r=>((r,t)=>{for(const e of r)if(j(e,"id")){const r=lr.get(e.id);r&&r.f[1](t)}})(c,r)])):mr(m).catch(a),r)return o();lr.set(l,{f:[o,a],stack:s})}))},c=e(!1);return c[N]=e(!0),c[N][N]=c[N],d(t)&&Object.defineProperty(r,t,{value:c,configurable:!0}),c}})}const q="rpc.async-iterator.",D=Symbol.for(q+"start"),F=Symbol.for(q+"next"),L=Symbol.for(q+"return"),U=Symbol.for(q+"throw");class B{__init(){this.d=!1}__init2(){this.c=async r=>(await Q(r,(()=>this.d=!0)),r)}constructor(r,t){this.r=r,this.i=t,B.prototype.__init.call(this),B.prototype.__init2.call(this)}async return(r){return this.d?V(!0,r):this.c(this.r[L](await this.i,r))}async next(r){return this.d?V(!0):this.c(this.r[F](await this.i,r))}async throw(r){if(!this.d)return this.c(this.r[U](await this.i,r));throw r}}const H=async function*(){};w(B,H.constructor.prototype);const K=Object.getPrototypeOf(H());w(B.prototype,K);const Q=async(r,t)=>{try{const e=await r;e&&e.done&&t()}catch(r){}},V=(r,t)=>({done:r,value:t});r.AsyncCall=G,r.AsyncGeneratorCall=(r,t)=>{const o=new Map,[s]=J(null==(i=t.strict)||i);var i;const{idGenerator:c=I}=t,l=(r,t)=>{const n=o.get(r);if(!n){if(s)throw a(e,Error(`Iterator ${r}, `));return C}const i=t(n);return Q(i,(()=>o.delete(r))),i},u=G({async[D](t,e){const n=(await r)[t];if(!p(n)){if(s)throw new TypeError(t+" is not a function");return C}const i=n(...e),a=c();return o.set(a,i),E(a)},[F]:(r,t)=>l(r,(r=>r.next(t))),[L]:(r,t)=>l(r,(r=>p(r.return)&&r.return(t))),[U]:(r,t)=>l(r,(r=>p(r.throw)&&r.throw(t)))},t);return new Proxy({__proto__:null},{get:(r,t)=>{if(!d(t))throw a(n,new TypeError(""));if(r[t])return r[t];const e=(...r)=>{const e=u[D](t,r);return new B(u,e)};return Object.defineProperty(r,t,{value:e,configurable:!0}),e}})},r.JSONSerialization=(r=[g,g],t,e="null")=>({serialization(n){if(e&&h(n)&&j(n,"result")&&n.result===g){const r={...n};r.result=null,"keep"===e&&(r.undef=!0),n=r}return JSON.stringify(n,r[0],t)},deserialization(t){const e=JSON.parse(t,r[1]);return h(e)&&j(e,"result")&&null===e.result&&j(e,"undef")&&!0===e.undef&&(e.result=g,delete e.undef),e}}),r.NoSerialization=z,r.batch=r=>{let t=[];return[new Proxy({__proto__:null},{get(e,n){if(d(n)&&e[n])return e[n];const o=(...e)=>r[R](t,n,...e);return(o[N]=(...e)=>r[R][N](t,n,...e))[N]=o[N],d(n)&&Object.defineProperty(e,n,{value:o,configurable:!0}),o}}),(r=t.r)=>r&&r[0](),(r=Error("Aborted"),e=t.r)=>{e&&e[1](r),t=[]}]},r.notify=r=>p(r)?r[N]:new Proxy(r,{get:T}),Object.defineProperty(r,"__esModule",{value:!0})})); | ||
//# sourceMappingURL=full.min.js.map |
{ | ||
"name": "async-call-rpc", | ||
"version": "5.0.0", | ||
"version": "5.1.0", | ||
"description": "A lightweight JSON RPC server & client", | ||
@@ -27,4 +27,4 @@ "main": "out/base.js", | ||
"release": "npm run clean && npm run test && npm run build && standard-version", | ||
"watch:tsc": "tsc -b ./tsconfig.es.json ./utils-src/tsconfig.es.json ./utils-src/tsconfig.node.json -w", | ||
"build:tsc": "tsc -b ./tsconfig.es.json ./utils-src/tsconfig.es.json ./utils-src/tsconfig.node.json", | ||
"watch:tsc": "tsc -b -w", | ||
"build:tsc": "tsc -b", | ||
"watch:rollup": "rollup -c -w", | ||
@@ -54,25 +54,26 @@ "build:rollup": "rollup -c", | ||
"devDependencies": { | ||
"@microsoft/api-documenter": "^7.11.0", | ||
"@microsoft/api-extractor": "^7.12.0", | ||
"@rollup/plugin-typescript": "^6.1.0", | ||
"@microsoft/api-documenter": "^7.12.14", | ||
"@microsoft/api-extractor": "^7.13.2", | ||
"@msgpack/msgpack": "^2.5.1", | ||
"@rollup/plugin-sucrase": "^3.1.0", | ||
"@types/bson": "^4.0.3", | ||
"@types/jest": "^26.0.15", | ||
"@types/node": "^14.14.10", | ||
"@types/jest": "^26.0.21", | ||
"@types/node": "^14.14.35", | ||
"@types/ws": "^7.4.0", | ||
"async-call-rpc": "link:", | ||
"bson": "^4.2.0", | ||
"bson": "^4.2.3", | ||
"jest": "^26.6.3", | ||
"jest-file-snapshot": "^0.5.0", | ||
"npm-run-all": "^4.1.5", | ||
"prettier": "^2.2.0", | ||
"prettier": "^2.2.1", | ||
"pretty-format": "^26.6.2", | ||
"rimraf": "^3.0.2", | ||
"rollup": "^2.33.3", | ||
"rollup-plugin-dts": "^2.0.0", | ||
"rollup": "^2.42.3", | ||
"rollup-plugin-dts": "^3.0.1", | ||
"rollup-plugin-terser": "^7.0.2", | ||
"standard-version": "^9.0.0", | ||
"ts-jest": "^26.4.4", | ||
"tslib": "^2.0.3", | ||
"typescript": "^4.1.2", | ||
"ws": "^7.4.0" | ||
"standard-version": "^9.1.1", | ||
"ts-jest": "^26.5.4", | ||
"tslib": "^2.1.0", | ||
"typescript": "^4.2.3", | ||
"ws": "^7.4.4" | ||
}, | ||
@@ -79,0 +80,0 @@ "files": [ |
@@ -238,6 +238,14 @@ # Async Call | ||
| Dependencies | [bson](https://npmjs.com/bson) | | ||
| Example (Node) | [./examples/node.websocket.server.js](https://github.com/Jack-Works/async-call-rpc/blob/master/examples/node.websocket.server.js) | | ||
| Example (Deno) | [./examples/deno.websocket.server.ts](https://github.com/Jack-Works/async-call-rpc/blob/master/examples/deno.websocket.server.ts) | | ||
| Example (Web) | [./examples/browser.websocket.client.js](https://github.com/Jack-Works/async-call-rpc/blob/master/examples/browser.websocket.client.js) | | ||
### (Web, Deno and Node) Msgpack | ||
| | Server | | ||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| Entry point Node | `async-call-rpc/utils/node/msgpack.js`<br />[(Source code)](https://github.com/Jack-Works/async-call-rpc/blob/master/utils-src/node/msgpack.ts) | | ||
| Entry point Browser/Deno | `https://cdn.jsdelivr.net/npm/async-call-rpc@latest/utils/web/msgpack.js`<br />[(Source code)](https://github.com/Jack-Works/async-call-rpc/blob/master/utils-src/web/msgpack.ts) | | ||
| Dependencies | [@msgpack/msgpack](https://npmjs.com/@msgpack/msgpack) | | ||
| Example (Node) | [./examples/node.websocket.server.js](https://github.com/Jack-Works/async-call-rpc/blob/master/examples/node.websocket.server.js) | | ||
| Example (Deno) | [./examples/deno.websocket.server.ts](https://github.com/Jack-Works/async-call-rpc/blob/master/examples/deno.websocket.server.ts) | | ||
| Example (Web) | [./examples/browser.websocket.client.js](https://github.com/Jack-Works/async-call-rpc/blob/master/examples/browser.websocket.client.js) | | ||
## Utils available if both server and client are created by this library | ||
@@ -244,0 +252,0 @@ |
@@ -9,3 +9,7 @@ /** | ||
import { isFunction, isString, Object_setPrototypeOf, Promise_resolve } from './utils/constants' | ||
import { HostedMessages, makeHostedMessage } from './utils/error' | ||
import { | ||
Err_Cannot_find_a_running_iterator_with_given_ID, | ||
Err_Only_string_can_be_the_RPC_method_name, | ||
makeHostedMessage, | ||
} from './utils/error' | ||
@@ -116,6 +120,3 @@ const i = 'rpc.async-iterator.' | ||
if (methodNotFound) | ||
throw makeHostedMessage( | ||
HostedMessages.AsyncCallGenerator_cannot_find_a_running_iterator_with_the_given_ID, | ||
new Error(`Iterator ${id}, `), | ||
) | ||
throw makeHostedMessage(Err_Cannot_find_a_running_iterator_with_given_ID, new Error(`Iterator ${id}, `)) | ||
else return AsyncCallIgnoreResponse | ||
@@ -152,3 +153,3 @@ } | ||
if (!isString(key)) | ||
throw makeHostedMessage(HostedMessages.Only_string_can_be_the_RPC_method_name, new TypeError('')) | ||
throw makeHostedMessage(Err_Only_string_can_be_the_RPC_method_name, new TypeError('')) | ||
if (cache[key]) return cache[key] | ||
@@ -155,0 +156,0 @@ const f = (...args: unknown[]) => { |
@@ -1,8 +0,2 @@ | ||
/** | ||
* See the document at https://github.com/Jack-Works/async-call/ | ||
*/ | ||
export type { CallbackBasedChannel, EventBasedChannel } from './types' | ||
export type { Serialization } from './utils/serialization' | ||
export type { Console } from './utils/console' | ||
export * from './types' | ||
export type { _IgnoreResponse } from './core/notify' | ||
@@ -13,4 +7,3 @@ export { JSONSerialization, NoSerialization } from './utils/serialization' | ||
import { Serialization, NoSerialization } from './utils/serialization' | ||
import type { Console } from './utils/console' | ||
import { NoSerialization } from './utils/serialization' | ||
import { | ||
@@ -30,234 +23,26 @@ Request, | ||
} from './utils/jsonrpc' | ||
import { removeStackHeader, RecoverError, makeHostedMessage, HostedMessages } from './utils/error' | ||
import { | ||
removeStackHeader, | ||
RecoverError, | ||
makeHostedMessage, | ||
Err_Cannot_call_method_starts_with_rpc_dot_directly, | ||
Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options, | ||
} from './utils/error' | ||
import { generateRandomID } from './utils/generateRandomID' | ||
import { normalizeStrictOptions, normalizeLogOptions } from './utils/normalizeOptions' | ||
import { AsyncCallIgnoreResponse, AsyncCallNotify, AsyncCallBatch } from './utils/internalSymbol' | ||
import { BatchQueue } from './core/batch' | ||
import { CallbackBasedChannel, EventBasedChannel } from './index' | ||
import { ERROR, isArray, isFunction, isString, Promise_reject, Promise_resolve, undefined } from './utils/constants' | ||
import type { BatchQueue } from './core/batch' | ||
import type { CallbackBasedChannel, EventBasedChannel, AsyncCallOptions, ConsoleInterface, _AsyncVersionOf } from './types' | ||
import { | ||
ERROR, | ||
isArray, | ||
isFunction, | ||
isString, | ||
Promise_reject, | ||
Promise_resolve, | ||
replayFunction, | ||
undefined, | ||
} from './utils/constants' | ||
/** | ||
* Log options of AsyncCall | ||
* @remarks | ||
* This option controls how AsyncCall should log RPC calls to the console. | ||
* @public | ||
*/ | ||
export interface AsyncCallLogLevel { | ||
/** | ||
* Log all requests to this instance | ||
* @defaultValue true | ||
*/ | ||
beCalled?: boolean | ||
/** | ||
* Log all errors produced when responding requests | ||
* @defaultValue true | ||
*/ | ||
localError?: boolean | ||
/** | ||
* Log remote errors | ||
* @defaultValue true | ||
*/ | ||
remoteError?: boolean | ||
/** | ||
* Send the call stack to the remote when making requests | ||
* @defaultValue false | ||
*/ | ||
sendLocalStack?: boolean | ||
/** | ||
* Control if AsyncCall should make the log better | ||
* @remarks | ||
* If use "pretty", it will call the logger with some CSS to make the log easier to read. | ||
* Check out this article to know more about it: {@link https://dev.to/annlin/consolelog-with-css-style-1mmp | Console.log with CSS Style} | ||
* @defaultValue 'pretty' | ||
*/ | ||
type?: 'basic' | 'pretty' | ||
/** | ||
* Log a function that allows to execute the request with same arguments again | ||
* @remarks | ||
* Do not use this options in the production environment because it will log a closure that captures the arguments of requests. This may cause memory leak. | ||
* @defaultValue false | ||
*/ | ||
requestReplay?: boolean | ||
} | ||
/** | ||
* Control the behavior that different from the JSON RPC spec. | ||
* @public | ||
*/ | ||
export interface AsyncCallStrictJSONRPC { | ||
/** | ||
* Controls if AsyncCall send an ErrorResponse when the requested method is not defined. | ||
* @remarks | ||
* Set this options to false, AsyncCall will ignore the request (but print a log) if the method is not defined. | ||
* @defaultValue true | ||
*/ | ||
methodNotFound?: boolean | ||
/** | ||
* Controls if AsyncCall send an ErrorResponse when the message is not valid. | ||
* @remarks | ||
* Set this options to false, AsyncCall will ignore the request that cannot be parsed as a valid JSON RPC payload. This is useful when the message channel is also used to transfer other kinds of messages. | ||
* @defaultValue true | ||
*/ | ||
unknownMessage?: boolean | ||
} | ||
/** | ||
* Options for {@link AsyncCall} | ||
* @public | ||
*/ | ||
export interface AsyncCallOptions { | ||
/** | ||
* This option is used for better log print. | ||
* @defaultValue `rpc` | ||
*/ | ||
key?: string | ||
/** | ||
* How to serialize and deserialize the JSON RPC payload | ||
* | ||
* @remarks | ||
* See {@link Serialization}. | ||
* There is some built-in serializer: | ||
* | ||
* - {@link NoSerialization} (Not doing anything to the message) | ||
* | ||
* - {@link JSONSerialization} (Using JSON.parse/stringify in the backend) | ||
* | ||
* - {@link https://github.com/jack-works/async-call-rpc#web-deno-and-node-bson BSONSerialization} (use the {@link https://npmjs.org/bson | bson} as the serializer) | ||
* | ||
* @defaultValue {@link NoSerialization} | ||
*/ | ||
serializer?: Serialization | ||
/** | ||
* Provide the logger of AsyncCall | ||
* @remarks | ||
* See {@link Console} | ||
* @defaultValue globalThis.console | ||
*/ | ||
logger?: Console | ||
/** | ||
* The message channel to exchange messages between server and client | ||
* @example | ||
* {@link https://github.com/Jack-Works/async-call-rpc/blob/master/utils-src/web/websocket.client.ts | Example for CallbackBasedChannel} or {@link https://github.com/Jack-Works/async-call-rpc/blob/master/utils-src/node/websocket.server.ts | Example for EventBasedChannel} | ||
*/ | ||
channel: CallbackBasedChannel | EventBasedChannel | ||
/** | ||
* Choose log level. | ||
* @remarks | ||
* - `true` is a reasonable default value, which means all options are the default options in the {@link AsyncCallLogLevel} | ||
* | ||
* - `false` is disable all logs | ||
* | ||
* - `"all"` is enable all logs (stronger than `true`). | ||
* @defaultValue true | ||
*/ | ||
log?: AsyncCallLogLevel | boolean | 'all' | ||
/** | ||
* Control the behavior that different from the JSON RPC spec. See {@link AsyncCallStrictJSONRPC} | ||
* @remarks | ||
* - `true` is to enable all strict options | ||
* - `false` is to disable all strict options | ||
* @defaultValue true | ||
*/ | ||
strict?: AsyncCallStrictJSONRPC | boolean | ||
/** | ||
* Choose flavor of parameter structures defined in the spec | ||
* @remarks | ||
* | ||
* See {@link https://www.jsonrpc.org/specification#parameter_structures} | ||
* | ||
* When using `by-name`, only first parameter of the requests are sent to the remote and it must be an object. | ||
* | ||
* @privateRemarks | ||
* TODO: review the edge cases when using "by-name". | ||
* @defaultValue "by-position" | ||
*/ | ||
parameterStructures?: 'by-position' | 'by-name' | ||
/** | ||
* Prefer local implementation than remote. | ||
* @remarks | ||
* If you call a RPC method and it is also defined in the local, open this flag will call the local implementation directly instead of send a RPC request. No logs / serialization will be performed if a local implementation is used. | ||
* @defaultValue false | ||
*/ | ||
preferLocalImplementation?: boolean | ||
/** | ||
* The ID generator of each JSON RPC request | ||
* @defaultValue () =\> Math.random().toString(36).slice(2) | ||
*/ | ||
idGenerator?(): string | number | ||
/** | ||
* Control how to report error response according to the exception | ||
*/ | ||
mapError?: ErrorMapFunction<unknown> | ||
/** | ||
* If the instance should be "thenable". | ||
* @defaultValue undefined | ||
* @remarks | ||
* If this options is set to `true`, it will return a `then` method normally (forwards the call to the remote). | ||
* | ||
* If this options is set to `false`, it will return `undefined` even the remote has a method called "then". | ||
* | ||
* If this options is set to `undefined`, it will return `undefined` and show a warning. You must explicitly set this option to `true` or `false` to dismiss the warning. | ||
* | ||
* The motivation of this option is to resolve the problem caused by Promise auto-unwrapping. | ||
* | ||
* Consider this code: | ||
* | ||
* ```ts | ||
* async function getRPC() { | ||
* return AsyncCall(...) | ||
* } | ||
* ``` | ||
* | ||
* According to the JS semantics, it will invoke the "then" method immediately on the returning instance which is unwanted in most scenarios. | ||
* | ||
* To avoid this problem, methods called "then" are omitted from the type signatures. Strongly suggest to not use "then" as your RPC method name. | ||
*/ | ||
thenable?: boolean | ||
} | ||
/** | ||
* JSON RPC Request object | ||
* @public | ||
*/ | ||
export type JSONRPCRequest = { | ||
jsonrpc: '2.0' | ||
id?: string | number | null | ||
method: string | ||
params: readonly unknown[] | object | ||
} | ||
/** | ||
* @public | ||
* @param error - The exception | ||
* @param request - The request object | ||
*/ | ||
export type ErrorMapFunction<T = unknown> = ( | ||
error: unknown, | ||
request: Readonly<JSONRPCRequest>, | ||
) => { | ||
code: number | ||
message: string | ||
data?: T | ||
} | ||
/** | ||
* Make all function in the type T becomes async functions and filtering non-Functions out. | ||
* | ||
* @remarks | ||
* Only generics signatures on function that returning an Promise<T> will be preserved due to the limitation of TypeScript. | ||
* | ||
* Method called `then` are intentionally removed because it is very likely to be a foot gun in promise auto-unwrap. | ||
* @internal | ||
*/ | ||
export type _AsyncVersionOf<T> = { | ||
readonly // Explicitly exclude key called "then" because it will cause problem in promise auto-unwrap. | ||
[key in keyof T as key extends 'then' ? never : T[key] extends Function ? key : never]: T[key] extends ( | ||
...args: any | ||
) => Promise<any> | ||
? T[key] // If it is returning Promise<any>, we use T[key] to preserve generics on function signatures | ||
: T[key] extends (...args: infer Args) => infer Return // otherwise we convert it to async functions | ||
? (...args: Args) => Promise<Return extends PromiseLike<infer U> ? U : Return> | ||
: never | ||
} | ||
/** | ||
* Create a RPC server & client. | ||
@@ -335,3 +120,3 @@ * | ||
warn: console_warn = console_log, | ||
} = (logger || console) as Console | ||
} = (logger || console) as ConsoleInterface | ||
type PromiseParam = [resolve: (value?: any) => void, reject: (reason?: any) => void] | ||
@@ -372,7 +157,9 @@ const requestContext = new Map<string | number, { f: PromiseParam; stack: string }>() | ||
] | ||
if (log_requestReplay) | ||
logArgs.push(() => { | ||
debugger | ||
return executor.apply(resolvedThisSideImplementationValue, args) | ||
}) | ||
if (log_requestReplay) { | ||
// This function will be logged to the console so it must be 1 line | ||
// prettier-ignore | ||
const replay = () => { debugger; return executor.apply(resolvedThisSideImplementationValue, args) } | ||
replay.toString = replayFunction | ||
logArgs.push(replay) | ||
} | ||
if (remoteStack) { | ||
@@ -537,3 +324,3 @@ console_groupCollapsed(...logArgs) | ||
makeHostedMessage( | ||
HostedMessages.Instance_is_treated_as_Promise_please_explicitly_mark_if_it_is_thenable_or_not_via_the_options, | ||
Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options, | ||
new TypeError('RPC used as Promise: '), | ||
@@ -561,6 +348,3 @@ ), | ||
return Promise_reject( | ||
makeHostedMessage( | ||
HostedMessages.Can_not_call_method_starts_with_rpc_dot_directly, | ||
new TypeError(), | ||
), | ||
makeHostedMessage(Err_Cannot_call_method_starts_with_rpc_dot_directly, new TypeError()), | ||
) | ||
@@ -601,2 +385,2 @@ return new Promise<void>((resolve, reject) => { | ||
// Assume a console object in global if there is no custom logger provided | ||
declare const console: Console | ||
declare const console: ConsoleInterface |
import { isString } from '../utils/constants' | ||
import { AsyncCallBatch, AsyncCallNotify } from '../utils/internalSymbol' | ||
import { Request } from '../utils/jsonrpc' | ||
import type { Request } from '../utils/jsonrpc' | ||
/** | ||
@@ -5,0 +5,0 @@ * Wrap the AsyncCall instance to use batch call. |
259
src/types.ts
/** | ||
* ! This file MUST NOT contain any import statement. | ||
* ! This file is part of public API of this package (for Deno users). | ||
*/ | ||
/** | ||
* This interface represents a "on message" - "send response" model. | ||
@@ -41,1 +46,255 @@ * @remarks | ||
} | ||
/** | ||
* Log options of AsyncCall | ||
* @remarks | ||
* This option controls how AsyncCall should log RPC calls to the console. | ||
* @public | ||
*/ | ||
export interface AsyncCallLogLevel { | ||
/** | ||
* Log all requests to this instance | ||
* @defaultValue true | ||
*/ | ||
beCalled?: boolean | ||
/** | ||
* Log all errors produced when responding requests | ||
* @defaultValue true | ||
*/ | ||
localError?: boolean | ||
/** | ||
* Log remote errors | ||
* @defaultValue true | ||
*/ | ||
remoteError?: boolean | ||
/** | ||
* Send the call stack to the remote when making requests | ||
* @defaultValue false | ||
*/ | ||
sendLocalStack?: boolean | ||
/** | ||
* Control if AsyncCall should make the log better | ||
* @remarks | ||
* If use "pretty", it will call the logger with some CSS to make the log easier to read. | ||
* Check out this article to know more about it: {@link https://dev.to/annlin/consolelog-with-css-style-1mmp | Console.log with CSS Style} | ||
* @defaultValue 'pretty' | ||
*/ | ||
type?: 'basic' | 'pretty' | ||
/** | ||
* Log a function that allows to execute the request with same arguments again | ||
* @remarks | ||
* Do not use this options in the production environment because it will log a closure that captures the arguments of requests. This may cause memory leak. | ||
* @defaultValue false | ||
*/ | ||
requestReplay?: boolean | ||
} | ||
/** | ||
* Control the behavior that different from the JSON RPC spec. | ||
* @public | ||
*/ | ||
export interface AsyncCallStrictJSONRPC { | ||
/** | ||
* Controls if AsyncCall send an ErrorResponse when the requested method is not defined. | ||
* @remarks | ||
* Set this options to false, AsyncCall will ignore the request (but print a log) if the method is not defined. | ||
* @defaultValue true | ||
*/ | ||
methodNotFound?: boolean | ||
/** | ||
* Controls if AsyncCall send an ErrorResponse when the message is not valid. | ||
* @remarks | ||
* Set this options to false, AsyncCall will ignore the request that cannot be parsed as a valid JSON RPC payload. This is useful when the message channel is also used to transfer other kinds of messages. | ||
* @defaultValue true | ||
*/ | ||
unknownMessage?: boolean | ||
} | ||
/** | ||
* Options for {@link AsyncCall} | ||
* @public | ||
*/ | ||
export interface AsyncCallOptions { | ||
/** | ||
* This option is used for better log print. | ||
* @defaultValue `rpc` | ||
*/ | ||
key?: string | ||
/** | ||
* How to serialize and deserialize the JSON RPC payload | ||
* | ||
* @remarks | ||
* See {@link Serialization}. | ||
* There is some built-in serializer: | ||
* | ||
* - {@link NoSerialization} (Not doing anything to the message) | ||
* | ||
* - {@link JSONSerialization} (Using JSON.parse/stringify in the backend) | ||
* | ||
* - {@link https://github.com/jack-works/async-call-rpc#web-deno-and-node-bson | BSONSerialization} (use the {@link https://npmjs.org/bson | bson} as the serializer) | ||
* | ||
* @defaultValue {@link NoSerialization} | ||
*/ | ||
serializer?: Serialization | ||
/** | ||
* Provide the logger of AsyncCall | ||
* @remarks | ||
* See {@link ConsoleInterface} | ||
* @defaultValue globalThis.console | ||
*/ | ||
logger?: ConsoleInterface | ||
/** | ||
* The message channel to exchange messages between server and client | ||
* @example | ||
* {@link https://github.com/Jack-Works/async-call-rpc/blob/master/utils-src/web/websocket.client.ts | Example for CallbackBasedChannel} or {@link https://github.com/Jack-Works/async-call-rpc/blob/master/utils-src/node/websocket.server.ts | Example for EventBasedChannel} | ||
*/ | ||
channel: CallbackBasedChannel | EventBasedChannel | ||
/** | ||
* Choose log level. | ||
* @remarks | ||
* - `true` is a reasonable default value, which means all options are the default options in the {@link AsyncCallLogLevel} | ||
* | ||
* - `false` is disable all logs | ||
* | ||
* - `"all"` is enable all logs (stronger than `true`). | ||
* @defaultValue true | ||
*/ | ||
log?: AsyncCallLogLevel | boolean | 'all' | ||
/** | ||
* Control the behavior that different from the JSON RPC spec. See {@link AsyncCallStrictJSONRPC} | ||
* @remarks | ||
* - `true` is to enable all strict options | ||
* - `false` is to disable all strict options | ||
* @defaultValue true | ||
*/ | ||
strict?: AsyncCallStrictJSONRPC | boolean | ||
/** | ||
* Choose flavor of parameter structures defined in the spec | ||
* @remarks | ||
* | ||
* See {@link https://www.jsonrpc.org/specification#parameter_structures} | ||
* | ||
* When using `by-name`, only first parameter of the requests are sent to the remote and it must be an object. | ||
* | ||
* @privateRemarks | ||
* TODO: review the edge cases when using "by-name". | ||
* @defaultValue "by-position" | ||
*/ | ||
parameterStructures?: 'by-position' | 'by-name' | ||
/** | ||
* Prefer local implementation than remote. | ||
* @remarks | ||
* If you call a RPC method and it is also defined in the local, open this flag will call the local implementation directly instead of send a RPC request. No logs / serialization will be performed if a local implementation is used. | ||
* @defaultValue false | ||
*/ | ||
preferLocalImplementation?: boolean | ||
/** | ||
* The ID generator of each JSON RPC request | ||
* @defaultValue () =\> Math.random().toString(36).slice(2) | ||
*/ | ||
idGenerator?(): string | number | ||
/** | ||
* Control how to report error response according to the exception | ||
*/ | ||
mapError?: ErrorMapFunction<unknown> | ||
/** | ||
* If the instance should be "thenable". | ||
* @defaultValue undefined | ||
* @remarks | ||
* If this options is set to `true`, it will return a `then` method normally (forwards the call to the remote). | ||
* | ||
* If this options is set to `false`, it will return `undefined` even the remote has a method called "then". | ||
* | ||
* If this options is set to `undefined`, it will return `undefined` and show a warning. You must explicitly set this option to `true` or `false` to dismiss the warning. | ||
* | ||
* The motivation of this option is to resolve the problem caused by Promise auto-unwrapping. | ||
* | ||
* Consider this code: | ||
* | ||
* ```ts | ||
* async function getRPC() { | ||
* return AsyncCall(...) | ||
* } | ||
* ``` | ||
* | ||
* According to the JS semantics, it will invoke the "then" method immediately on the returning instance which is unwanted in most scenarios. | ||
* | ||
* To avoid this problem, methods called "then" are omitted from the type signatures. Strongly suggest to not use "then" as your RPC method name. | ||
*/ | ||
thenable?: boolean | ||
} | ||
/** | ||
* JSON RPC Request object | ||
* @public | ||
*/ | ||
export type JSONRPCRequest = { | ||
jsonrpc: '2.0' | ||
id?: string | number | null | ||
method: string | ||
params: readonly unknown[] | object | ||
} | ||
/** | ||
* @public | ||
* @param error - The exception | ||
* @param request - The request object | ||
*/ | ||
export type ErrorMapFunction<T = unknown> = ( | ||
error: unknown, | ||
request: Readonly<JSONRPCRequest>, | ||
) => { | ||
code: number | ||
message: string | ||
data?: T | ||
} | ||
/** | ||
* Make all function in the type T becomes async functions and filtering non-Functions out. | ||
* | ||
* @remarks | ||
* Only generics signatures on function that returning an Promise<T> will be preserved due to the limitation of TypeScript. | ||
* | ||
* Method called `then` are intentionally removed because it is very likely to be a foot gun in promise auto-unwrap. | ||
* @internal | ||
*/ | ||
export type _AsyncVersionOf<T> = { | ||
readonly // Explicitly exclude key called "then" because it will cause problem in promise auto-unwrap. | ||
[key in keyof T as key extends 'then' ? never : T[key] extends Function ? key : never]: T[key] extends ( | ||
...args: any | ||
) => Promise<any> | ||
? T[key] // If it is returning Promise<any>, we use T[key] to preserve generics on function signatures | ||
: T[key] extends (...args: infer Args) => infer Return // otherwise we convert it to async functions | ||
? (...args: Args) => Promise<Return extends PromiseLike<infer U> ? U : Return> | ||
: never | ||
} | ||
/** | ||
* The minimal Console interface that AsyncCall needs. | ||
* @public | ||
* @remarks | ||
* The method not provided will use "log" as it's fallback. | ||
*/ | ||
export interface ConsoleInterface { | ||
warn?(...args: unknown[]): void | ||
debug?(...args: unknown[]): void | ||
log(...args: unknown[]): void | ||
groupCollapsed?(...args: unknown[]): void | ||
groupEnd?(...args: unknown[]): void | ||
error?(...args: unknown[]): void | ||
} | ||
export type { ConsoleInterface as Console } | ||
/** | ||
* Serialize and deserialize of the JSON RPC payload | ||
* @public | ||
*/ | ||
export interface Serialization { | ||
/** | ||
* Serialize data | ||
* @param from - original data | ||
*/ | ||
serialization(from: any): unknown | PromiseLike<unknown> | ||
/** | ||
* Deserialize data | ||
* @param serialized - Serialized data | ||
*/ | ||
deserialization(serialized: unknown): unknown | PromiseLike<unknown> | ||
} |
@@ -11,1 +11,2 @@ export const isString = (x: unknown): x is string => typeof x === 'string' | ||
export const isArray = Array.isArray | ||
export const replayFunction = () => '() => replay()' |
@@ -6,11 +6,16 @@ class CustomError extends Error { | ||
} | ||
export const Err_Cannot_find_a_running_iterator_with_given_ID = {} as Symbol | ||
export const Err_Only_string_can_be_the_RPC_method_name = {} as Symbol | ||
export const Err_Cannot_call_method_starts_with_rpc_dot_directly = {} as Symbol | ||
export const Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options = {} as Symbol | ||
const Messages = [ | ||
Err_Cannot_find_a_running_iterator_with_given_ID, | ||
Err_Only_string_can_be_the_RPC_method_name, | ||
Err_Cannot_call_method_starts_with_rpc_dot_directly, | ||
Err_Then_is_accessed_on_local_implementation_Please_explicitly_mark_if_it_is_thenable_in_the_options, | ||
] | ||
// https://github.com/Jack-Works/async-call-rpc/wiki/Error-messages | ||
export const enum HostedMessages { | ||
AsyncCallGenerator_cannot_find_a_running_iterator_with_the_given_ID, | ||
Only_string_can_be_the_RPC_method_name, | ||
Can_not_call_method_starts_with_rpc_dot_directly, | ||
Instance_is_treated_as_Promise_please_explicitly_mark_if_it_is_thenable_or_not_via_the_options, | ||
} | ||
export function makeHostedMessage(id: HostedMessages, error: Error) { | ||
error.message += `Error ${id}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#` + id | ||
export function makeHostedMessage(id: Symbol, error: Error) { | ||
const n = Messages.indexOf(id) | ||
error.message += `Error ${n}: https://github.com/Jack-Works/async-call-rpc/wiki/Errors#` + n | ||
return error | ||
@@ -42,3 +47,3 @@ } | ||
} else if (type in errors) { | ||
const e = new errors[type](message) | ||
const e = new errors[type]!(message) | ||
e.stack = stack | ||
@@ -45,0 +50,0 @@ // @ts-ignore |
import { globalDOMException as DOMException, DOMExceptionHeader } from './error' | ||
import { ErrorMapFunction } from '../Async-Call' | ||
import type { ErrorMapFunction } from '../Async-Call' | ||
import { ERROR, isArray, isBoolean, isFunction, isObject, isString, undefined } from './constants' | ||
@@ -4,0 +4,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { AsyncCallOptions } from '../Async-Call' | ||
import type { AsyncCallOptions } from '../Async-Call' | ||
import { isBoolean } from './constants' | ||
@@ -3,0 +3,0 @@ const undefinedToTrue = (x: undefined | boolean) => (x === void 0 ? true : x) |
@@ -5,19 +5,4 @@ //#region Serialization | ||
import { undefined } from './constants' | ||
import type { Serialization } from '../types' | ||
/** | ||
* Serialize and deserialize of the JSON RPC payload | ||
* @public | ||
*/ | ||
export interface Serialization { | ||
/** | ||
* Serialize data | ||
* @param from - original data | ||
*/ | ||
serialization(from: any): unknown | PromiseLike<unknown> | ||
/** | ||
* Deserialize data | ||
* @param serialized - Serialized data | ||
*/ | ||
deserialization(serialized: unknown): unknown | PromiseLike<unknown> | ||
} | ||
@@ -24,0 +9,0 @@ /** |
{ | ||
"extends": "./tsconfig.base.json", | ||
"compilerOptions": { | ||
"target": "es2018" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, | ||
"module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, | ||
"lib": ["ES2018"] /* Specify library files to be included in the compilation. */, | ||
/* Strict Type-Checking Options */ | ||
"strict": true /* Enable all strict type-checking options. */, | ||
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, | ||
"strictNullChecks": true /* Enable strict null checks. */, | ||
"strictFunctionTypes": true /* Enable strict checking of function types. */, | ||
"strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */, | ||
"strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, | ||
"noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, | ||
"alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, | ||
/* Additional Checks */ | ||
// "noUnusedLocals": true /* Report errors on unused locals. */, | ||
// "noUnusedParameters": true /* Report errors on unused parameters. */, | ||
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */, | ||
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, | ||
"rootDir": "./src/", | ||
// This lib does not require any env | ||
"typeRoots": [], | ||
"types": [], | ||
"esModuleInterop": true | ||
"tsBuildInfoFile": "./temp/solution.tsbuildinfo", | ||
}, | ||
"include": ["./src/**/*.ts"], | ||
"ts-node": { | ||
"transpileOnly": true, | ||
"compilerOptions": { "module": "CommonJS" } | ||
} | ||
"compilerOptions": { "module": "CommonJS", "esModuleInterop": true }, | ||
"transpileOnly": true | ||
}, | ||
"references": [ | ||
{ "path": "./src/tsconfig.json" }, | ||
{ "path": "./utils-src/tsconfig.node.json" }, | ||
{ "path": "./utils-src/tsconfig.es.json" } | ||
], | ||
"files": [] | ||
} |
@@ -1,11 +0,19 @@ | ||
import type bsonLib from 'bson' | ||
import type { serialize as S, deserialize as D } from 'bson' | ||
import type { Serialization } from 'async-call-rpc' | ||
export const BSON_Serialization = (bson: typeof bsonLib): Serialization => ({ | ||
export const BSON_Serialization = ( | ||
{ | ||
deserialize, | ||
serialize, | ||
}: { | ||
serialize: typeof S | ||
deserialize: typeof D | ||
} = require('bson'), | ||
): Serialization => ({ | ||
deserialization(data: Buffer) { | ||
return bson.deserialize(data) | ||
return deserialize(data) | ||
}, | ||
serialization(data: Buffer) { | ||
return bson.serialize(data) | ||
serialization(data: any) { | ||
return serialize(data) | ||
}, | ||
}) |
{ | ||
"extends": "../tsconfig.base.json", | ||
"compilerOptions": { | ||
/* Basic Options */ | ||
// "incremental": true, /* Enable incremental compilation */ | ||
"target": "ES2018" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, | ||
"module": "ESNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, | ||
"lib": ["ES2018", "DOM", "DOM.Iterable"] /* Specify library files to be included in the compilation. */, | ||
// "allowJs": true, /* Allow javascript files to be compiled. */ | ||
// "checkJs": true, /* Report errors in .js files. */ | ||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ | ||
"declaration": true /* Generates corresponding '.d.ts' file. */, | ||
"declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, | ||
"sourceMap": true /* Generates corresponding '.map' file. */, | ||
// "outFile": "./", /* Concatenate and emit output to single file. */ | ||
"outDir": "../utils/" /* Redirect output structure to the directory. */, | ||
"rootDir": "./" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | ||
// "composite": true, /* Enable project compilation */ | ||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ | ||
// TODO: wait for https://github.com/microsoft/TypeScript/issues/38427 | ||
// "removeComments": true /* Do not emit comments to output. */, | ||
// "noEmit": true, /* Do not emit outputs. */ | ||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */ | ||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ | ||
"isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, | ||
/* Strict Type-Checking Options */ | ||
"strict": true /* Enable all strict type-checking options. */, | ||
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, | ||
"strictNullChecks": true /* Enable strict null checks. */, | ||
"strictFunctionTypes": true /* Enable strict checking of function types. */, | ||
"strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */, | ||
"strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, | ||
"noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, | ||
"alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, | ||
/* Additional Checks */ | ||
// "noUnusedLocals": true, /* Report errors on unused locals. */ | ||
// "noUnusedParameters": true, /* Report errors on unused parameters. */ | ||
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */, | ||
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, | ||
/* Module Resolution Options */ | ||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ | ||
"baseUrl": "./" /* Base directory to resolve non-absolute module names. */, | ||
"module": "ESNext", | ||
"lib": ["ES2018", "DOM", "DOM.Iterable"], | ||
"outDir": "../utils/", | ||
"rootDir": "./", | ||
"baseUrl": "./", | ||
"paths": { | ||
"async-call-rpc": ["../src/Async-Call"] | ||
} /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, | ||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ | ||
"typeRoots": [] /* List of folders to include type definitions from. */, | ||
"types": [] /* Type declaration files to be included in compilation. */, | ||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | ||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, | ||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ | ||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | ||
/* Source Map Options */ | ||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ | ||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ | ||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ | ||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ | ||
/* Experimental Options */ | ||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ | ||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ | ||
/* Advanced Options */ | ||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, | ||
"importsNotUsedAsValues": "error", | ||
"composite": true, | ||
}, | ||
"tsBuildInfoFile": "../temp/web.tsbuildinfo" | ||
}, | ||
"include": ["./web/**/*.ts"], | ||
"references": [{ "path": "../tsconfig.es.json" }] | ||
"references": [{ "path": "../src/tsconfig.json" }] | ||
} |
{ | ||
"extends": "../tsconfig.base.json", | ||
"compilerOptions": { | ||
/* Basic Options */ | ||
// "incremental": true, /* Enable incremental compilation */ | ||
"target": "ES2018" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, | ||
"module": "CommonJS" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, | ||
"lib": ["ES2018"] /* Specify library files to be included in the compilation. */, | ||
// "allowJs": true, /* Allow javascript files to be compiled. */ | ||
// "checkJs": true, /* Report errors in .js files. */ | ||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ | ||
"declaration": true /* Generates corresponding '.d.ts' file. */, | ||
"declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, | ||
"sourceMap": true /* Generates corresponding '.map' file. */, | ||
// "outFile": "./", /* Concatenate and emit output to single file. */ | ||
"outDir": "../utils/" /* Redirect output structure to the directory. */, | ||
"rootDir": "./" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | ||
// "composite": true, /* Enable project compilation */ | ||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ | ||
// TODO: wait for https://github.com/microsoft/TypeScript/issues/38427 | ||
// "removeComments": true /* Do not emit comments to output. */, | ||
// "noEmit": true, /* Do not emit outputs. */ | ||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */ | ||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ | ||
"isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, | ||
/* Strict Type-Checking Options */ | ||
"strict": true /* Enable all strict type-checking options. */, | ||
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, | ||
"strictNullChecks": true /* Enable strict null checks. */, | ||
"strictFunctionTypes": true /* Enable strict checking of function types. */, | ||
"strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */, | ||
"strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, | ||
"noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, | ||
"alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, | ||
/* Additional Checks */ | ||
// "noUnusedLocals": true, /* Report errors on unused locals. */ | ||
// "noUnusedParameters": true, /* Report errors on unused parameters. */ | ||
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */, | ||
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, | ||
/* Module Resolution Options */ | ||
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, | ||
"baseUrl": "./" /* Base directory to resolve non-absolute module names. */, | ||
"module": "CommonJS", | ||
"lib": ["ES2018"], | ||
"outDir": "../utils/", | ||
"rootDir": "./", | ||
"baseUrl": "./", | ||
"esModuleInterop": true, | ||
"paths": { | ||
"async-call-rpc": ["../src/Async-Call"] | ||
} /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, | ||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ | ||
// "typeRoots": [] /* List of folders to include type definitions from. */, | ||
// "types": [] /* Type declaration files to be included in compilation. */, | ||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | ||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, | ||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ | ||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | ||
/* Source Map Options */ | ||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ | ||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ | ||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ | ||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ | ||
/* Experimental Options */ | ||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ | ||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ | ||
/* Advanced Options */ | ||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, | ||
"importsNotUsedAsValues": "error", | ||
"composite": true, | ||
}, | ||
"tsBuildInfoFile": "../temp/node.tsbuildinfo" | ||
}, | ||
"include": ["./node/**/*.ts"], | ||
"references": [{ "path": "../tsconfig.es.json" }] | ||
"references": [{ "path": "../src/tsconfig.json" }] | ||
} |
import type { Serialization } from 'async-call-rpc' | ||
export const BSON_Serialization = (bson: typeof import('bson')): Serialization => ({ | ||
import type { serialize as S, deserialize as D } from 'bson' | ||
export const BSON_Serialization = ({ | ||
deserialize, | ||
serialize, | ||
}: { | ||
serialize: typeof S | ||
deserialize: typeof D | ||
}): Serialization => ({ | ||
async deserialization(data: unknown) { | ||
if (data instanceof Blob) data = await data.arrayBuffer() | ||
if (data instanceof ArrayBuffer) data = new Uint8Array(data) | ||
// @ts-ignore | ||
return bson.deserialize(data) | ||
return deserialize(data as any) | ||
}, | ||
serialization(data: any) { | ||
// @ts-ignore | ||
return bson.serialize(data) | ||
return serialize(data) | ||
}, | ||
}) |
@@ -1,4 +0,7 @@ | ||
import type bsonLib from 'bson'; | ||
import type { serialize as S, deserialize as D } from 'bson'; | ||
import type { Serialization } from 'async-call-rpc'; | ||
export declare const BSON_Serialization: (bson: typeof bsonLib) => Serialization; | ||
export declare const BSON_Serialization: ({ deserialize, serialize, }?: { | ||
serialize: typeof S; | ||
deserialize: typeof D; | ||
}) => Serialization; | ||
//# sourceMappingURL=bson.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.BSON_Serialization = void 0; | ||
const BSON_Serialization = (bson) => ({ | ||
const BSON_Serialization = ({ deserialize, serialize, } = require('bson')) => ({ | ||
deserialization(data) { | ||
return bson.deserialize(data); | ||
return deserialize(data); | ||
}, | ||
serialization(data) { | ||
return bson.serialize(data); | ||
return serialize(data); | ||
}, | ||
@@ -11,0 +11,0 @@ }); |
@@ -6,3 +6,8 @@ "use strict"; | ||
constructor(server) { | ||
this.server = server; | ||
Object.defineProperty(this, "server", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: server | ||
}); | ||
} | ||
@@ -9,0 +14,0 @@ setup(callback) { |
import type { Serialization } from 'async-call-rpc'; | ||
export declare const BSON_Serialization: (bson: typeof import('bson')) => Serialization; | ||
import type { serialize as S, deserialize as D } from 'bson'; | ||
export declare const BSON_Serialization: ({ deserialize, serialize, }: { | ||
serialize: typeof S; | ||
deserialize: typeof D; | ||
}) => Serialization; | ||
//# sourceMappingURL=bson.d.ts.map |
@@ -1,2 +0,2 @@ | ||
export const BSON_Serialization = (bson) => ({ | ||
export const BSON_Serialization = ({ deserialize, serialize, }) => ({ | ||
async deserialization(data) { | ||
@@ -7,10 +7,8 @@ if (data instanceof Blob) | ||
data = new Uint8Array(data); | ||
// @ts-ignore | ||
return bson.deserialize(data); | ||
return deserialize(data); | ||
}, | ||
serialization(data) { | ||
// @ts-ignore | ||
return bson.serialize(data); | ||
return serialize(data); | ||
}, | ||
}); | ||
//# sourceMappingURL=bson.js.map |
@@ -6,3 +6,8 @@ export class WorkerChannel { | ||
constructor(worker = self) { | ||
this.worker = worker; | ||
Object.defineProperty(this, "worker", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: worker | ||
}); | ||
} | ||
@@ -9,0 +14,0 @@ on(listener) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
85
6309
330
0
502827
24