Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

async-call-rpc

Package Overview
Dependencies
Maintainers
1
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

async-call-rpc - npm Package Compare versions

Comparing version 2.0.2 to 3.0.0

22

CHANGELOG.md

@@ -5,2 +5,24 @@ # Changelog

## [3.0.0](https://github.com/Jack-Works/async-call/compare/v2.0.2...v3.0.0) (2020-06-27)
### ⚠ BREAKING CHANGES
- Enable strict mode by default, if you need the non-strict behavior, switch "strict" to false.
- Move `strict.noUndefined` to 3rd parameter of JSONSerialization since only JSON needs it.
- The export version changed from `full` to `base`. If you need full version, please import from `async-call-rpc/full`.
### Features
* Add hook for custom Error data close [#8](https://github.com/Jack-Works/async-call/issues/8) ([2ab36bb](https://github.com/Jack-Works/async-call/commit/2ab36bb06c259ca7161a79f4ea649e15939f0966))
* change to strict by default, move undefined keeping to JSONSerialization ([d860da5](https://github.com/Jack-Works/async-call/commit/d860da52a88279fbadbb44982009ffc947426437))
* export base version by default ([6a11550](https://github.com/Jack-Works/async-call/commit/6a115507197a79694cd94a3dab6a517f913ff8ab))
* id generator, close [#13](https://github.com/Jack-Works/async-call/issues/13) ([d3c51b5](https://github.com/Jack-Works/async-call/commit/d3c51b59a7876bd0f14a76d8ee40a8dade5c65f2))
* preserve this binding, close [#16](https://github.com/Jack-Works/async-call/issues/16) ([69f1077](https://github.com/Jack-Works/async-call/commit/69f1077b6308e36aa99870c0257f2ed33897aef8))
### Bug Fixes
* server ignore sendLocalStack. close [#18](https://github.com/Jack-Works/async-call/issues/18) ([25629d3](https://github.com/Jack-Works/async-call/commit/25629d3f8ad74d23fb8a23184927117abf1ff725))
### [2.0.2](https://github.com/Jack-Works/async-call/compare/v2.0.1...v2.0.2) (2020-06-10)

@@ -7,0 +29,0 @@

86

out/base.d.ts

@@ -28,6 +28,14 @@ /**

* @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?: [Parameters<JSON['stringify']>[1], Parameters<JSON['parse']>[1]], space?: string | number | undefined) => Serialization;
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;

@@ -39,7 +47,7 @@ /**

interface Console {
debug(...args: unknown[]): void;
debug?(...args: unknown[]): void;
log(...args: unknown[]): void;
groupCollapsed(...args: unknown[]): void;
groupEnd(...args: unknown[]): void;
error(...args: unknown[]): void;
groupCollapsed?(...args: unknown[]): void;
groupEnd?(...args: unknown[]): void;
error?(...args: unknown[]): void;
}

@@ -84,14 +92,9 @@

/**
* Return an error when the requested method is not defined
* @defaultValue false
* Return an error when the requested method is not defined, otherwise, ignore the request.
* @defaultValue true
*/
methodNotFound?: boolean;
/**
* don't try to keep `undefined` result (then it will be `null`)
* @defaultValue false
*/
noUndefined?: boolean;
/**
* send an error when receive invalid JSON RPC payload
* @defaultValue false
* @defaultValue true
*/

@@ -101,2 +104,10 @@ unknownMessage?: boolean;

/**
* The message channel interface that allows
* @public
*/
interface MessageChannel {
on(event: string, eventListener: (data: unknown) => void): void;
emit(event: string, data: unknown): void;
}
/**
* Options for {@link AsyncCall}

@@ -123,3 +134,3 @@ * @public

*/
key: string;
key?: string;
/**

@@ -138,3 +149,3 @@ * How to serialization and deserialization JSON RPC payload

*/
serializer: Serialization;
serializer?: Serialization;
/**

@@ -146,3 +157,3 @@ * The logger of AsyncCall

*/
logger: Console;
logger?: Console;
/**

@@ -162,6 +173,3 @@ * The message channel can let you transport messages between server and client

*/
messageChannel: {
on(event: string, callback: (data: unknown) => void): void;
emit(event: string, data: unknown): void;
};
messageChannel: MessageChannel;
/**

@@ -171,3 +179,3 @@ * Choose log level. See {@link AsyncCallLogLevel}

*/
log: AsyncCallLogLevel | boolean;
log?: AsyncCallLogLevel | boolean;
/**

@@ -177,3 +185,3 @@ * Strict options. See {@link AsyncCallStrictJSONRPC}

*/
strict: AsyncCallStrictJSONRPC | boolean;
strict?: AsyncCallStrictJSONRPC | boolean;
/**

@@ -185,3 +193,3 @@ * How parameters passed to remote

*/
parameterStructures: 'by-position' | 'by-name';
parameterStructures?: 'by-position' | 'by-name';
/**

@@ -193,3 +201,3 @@ * Prefer local implementation than remote.

*/
preferLocalImplementation: boolean;
preferLocalImplementation?: boolean;
/**

@@ -206,5 +214,29 @@ * (Browser) Try to preserve the browser "pause on uncaught exception".

*/
preservePauseOnException: boolean;
preservePauseOnException?: boolean;
/**
* The ID generator of each JSON RPC request
* @defaultValue () => Math.random().toString(36).slice(2)
*/
idGenerator?(): string | number;
/**
* Control the error response data
* @param error The happened Error
* @param request The request object
*/
mapError?: ErrorMapFunction<unknown>;
}
/**
* @public
*/
declare type ErrorMapFunction<T = unknown> = (error: unknown, request: Readonly<{
jsonrpc: '2.0';
id?: string | number | null;
method: string;
params: readonly unknown[] | object;
}>) => {
code: number;
message: string;
data?: T;
};
/**
* Make all function in the type T Async

@@ -234,4 +266,4 @@ * @internal

*/
declare function AsyncCall<OtherSideImplementedFunctions = {}>(thisSideImplementation: object | Promise<object> | undefined, options: Partial<AsyncCallOptions> & Pick<AsyncCallOptions, 'messageChannel'>): _AsyncVersionOf<OtherSideImplementedFunctions>;
declare function AsyncCall<OtherSideImplementedFunctions = {}>(thisSideImplementation: object | Promise<object> | undefined, options: AsyncCallOptions): _AsyncVersionOf<OtherSideImplementedFunctions>;
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, Console, JSONSerialization, NoSerialization, Serialization, _AsyncVersionOf };
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, Console, ErrorMapFunction, JSONSerialization, MessageChannel, NoSerialization, Serialization, _AsyncVersionOf };
/// <reference types="./base.d.ts" />
((e,r)=>{"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((e=e||self).AsyncCall={})})(this,(function(e){"use strict"
const r={serialization:async e=>e,deserialization:async e=>e}
class t extends Error{constructor(e,r,t,n){super(r),this.name=e,this.code=t,this.stack=n}}const n={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError}
function o(e=""){return e.replace(/^.+\n.+\n/,"")}const i=(()=>{const e=Reflect.get(globalThis,"DOMException")
class r extends Error{constructor(e,r,t,o){super(r),this.name=e,this.code=t,this.stack=o}}const t={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError}
function o(e=""){return e.replace(/^.+\n.+\n/,"")}const n=(()=>{const e=Reflect.get(globalThis,"DOMException")
if("function"==typeof e)return e})()
function a(e,r,t,n,o){let a=((e,r)=>{let t="Error"
try{t=null===(i=null===(n=o)||void 0===n?void 0:n.constructor)||void 0===i?void 0:i.name}catch(n){}var n,i
return"string"!=typeof t?"Error":t})()
return i&&o instanceof i&&(a="DOMException:"+o.name),"string"!=typeof o&&"number"!=typeof o&&"boolean"!=typeof o&&"bigint"!=typeof o||(a="Error",t=o+""),void 0===e&&(e=null),Number.isNaN(r=Math.floor(r))&&(r=-1),{error:{code:r,message:t,data:{stack:n,type:a}},id:e,jsonrpc:"2.0"}}function s(e){if(!l(e))return!1
if(!c(e,"jsonrpc"))return!1
function i(e,r,t,o){void 0===e&&(e=null),Number.isNaN(r=Math.floor(r))&&(r=-1)
const n={jsonrpc:"2.0",id:e,error:{code:r,message:t,data:o}}
return d(n.error,"data"),n}function a(e,r,t){const{id:o}=e,{code:n,message:a,data:s}=t(r,e)
return i(o,n,a,s)}i.ParseError=(e,r)=>{const t=a({},e,r),o=t.error
return o.code=-32700,o.message="Parse error",t},i.InvalidRequest=e=>i(e,-32600,"Invalid Request"),i.MethodNotFound=e=>i(e,-32601,"Method not found")
const s=(e="",r=-1)=>t=>{let o=""
l(t)&&u(t,"message")&&"string"==typeof t.message&&(o=t.message)
let i=((e,r)=>{let o="Error"
try{o=null===(i=null===(n=t)||void 0===n?void 0:n.constructor)||void 0===i?void 0:i.name}catch(n){}var n,i
return"string"!=typeof o?"Error":o})()
return n&&t instanceof n&&(i="DOMException:"+t.name),"string"!=typeof t&&"number"!=typeof t&&"boolean"!=typeof t&&"bigint"!=typeof t||(i="Error",o=t+""),{code:r,message:o,data:e?{stack:e,type:i}:{type:i}}}
function c(e){if(!l(e))return!1
if(!u(e,"jsonrpc"))return!1
if("2.0"!==e.jsonrpc)return!1
if(c(e,"params")){const r=e.params
if(!Array.isArray(r)&&!l(r))return!1}return!0}function l(e){return"object"==typeof e&&null!==e}function c(e,r){return r in e}a.ParseError=(e="")=>a(null,-32700,"Parse error",e),a.InvalidRequest=e=>a(e,-32600,"Invalid Request",""),a.MethodNotFound=e=>a(e,-32601,"Method not found",""),a.InvalidParams=e=>a(e,-32602,"Invalid params",""),a.InternalError=(e,r="")=>a(e,-32603,"Internal error"+r,"")
const d=Symbol.for("AsyncCall: This response should be ignored.")
async function u(e,r,t){const n=document.createElement("iframe"),i=new Promise((i,a)=>{try{n.style.display="none",document.body.appendChild(n)
{const s=n.contentDocument,l=n.contentWindow,c=s.createElement("button")
s.body.appendChild(c),c.onclick=()=>new l.Promise(n=>{(async()=>{e(o(Error().stack)),i(await r(...t))})().then(n)}),l.addEventListener("unhandledrejection",e=>a(e.reason)),c.click()}}catch(e){return console.error("Please close preservePauseOnException.",e),i(r(...t))}})
return i.finally(()=>n.remove()),i}const f={serializer:r,key:"default-jsonrpc",strict:!1,log:!0,parameterStructures:"by-position",preferLocalImplementation:!1,preservePauseOnException:!1}
e.AsyncCall=(e={},r)=>{let y=void 0
Promise.resolve(e).then(e=>y=e)
const{serializer:p,key:m,strict:v,log:E,parameterStructures:g,preferLocalImplementation:h,preservePauseOnException:b}={...f,...r},k=r.messageChannel,{methodNotFound:w=!1,noUndefined:$=!1,unknownMessage:P=!1}=(e=>"boolean"!=typeof e?e:{methodNotFound:e,unknownMessage:e,noUndefined:e})(v),{beCalled:S=!0,localError:j=!0,remoteError:A=!0,type:I="pretty",sendLocalStack:O=!1}=(e=>"boolean"!=typeof e?e:{beCalled:e,localError:e,remoteError:e,type:e?"pretty":"basic"})(E),x=(e=>{const r=e||globalThis.console,t=(...e)=>r.log(...e)
return Object.assign({},{debug:t,error:t,groupCollapsed:t,groupEnd:t,log:t},r)})(r.logger),C=new Map
async function R(e){var r,o,a
let s="",l="",d=0,u="Error"
if(c(e,"error")&&(s=e.error.message,d=e.error.code,l=null!==(o=null===(r=e.error.data)||void 0===r?void 0:r.stack)&&void 0!==o?o:"<remote stack not available>",u=(null===(a=e.error.data)||void 0===a?void 0:a.type)||"Error",A&&("basic"===I?x.error(`${u}: ${s}(${d}) @${e.id}\n${l}`):x.error(`${u}: ${s}(${d}) %c@${e.id}\n%c${l}`,"color: gray",""))),null===e.id||void 0===e.id)return
const{f:[f,y],stack:p}=C.get(e.id)||{stack:"",f:[null,null]}
f&&(C.delete(e.id),c(e,"error")?y(((e,r,o,a)=>{try{if(e.startsWith("DOMException:")&&i){const[,t]=e.split("DOMException:")
return new i(r,t)}if(e in n){const t=new n[e](r)
return t.stack=a,Object.assign(t,{code:o}),t}return new t(e,r,o,a)}catch(t){return Error(`E${o} ${e}: ${r}\n${a}`)}})(u,s,d,l+"\n аt AsyncCall (rpc) \n"+p)):f(e.result))}return k.on(m,async e=>{let r,t=void 0
try{if(r=await p.deserialization(e),s(r))t=await M(r),t&&await n(t)
else if(Array.isArray(r)&&r.every(s)&&0!==r.length){const e=await Promise.all(r.map(M))
if(r.every(e=>void 0===e))return
await n(e.filter(e=>e))}else P&&await n(a.InvalidRequest(r.id||null))}catch(e){x.error(e,r,t),n(a.ParseError(null==e?void 0:e.stack))}async function n(e){if(Array.isArray(e)){const r=e.map(e=>e).filter(e=>void 0!==e.id)
if(u(e,"params")){const r=e.params
if(!Array.isArray(r)&&!l(r))return!1}return!0}function l(e){return"object"==typeof e&&null!==e}function u(e,r){return r in e}function d(e,r){void 0===e[r]&&delete e[r]}const f={serialization:e=>e,deserialization:e=>e},p=Symbol.for("AsyncCall/ignored")
async function y(e,r,t,n){return new Promise((i,a)=>{var s
let c={}
try{c=document.createElement("iframe"),c.style.display="none",document.body.appendChild(c)
{const s=c.contentDocument,l=c.contentWindow,u=s.createElement("button")
s.body.appendChild(u),u.onclick=()=>new l.Promise(a=>{(async()=>{e(o(Error().stack)),i(await r.apply(t,n))})().then(a)}),l.onunhandledrejection=e=>a(e.reason),u.click()}}catch(e){try{console.error("Please close preservePauseOnException.",e)}catch(e){}return i(r(...n))}finally{null===(s=null==c?void 0:c.remove)||void 0===s||s.call(c)}})}const m={serializer:f,key:"default-jsonrpc",strict:!0,log:!0,parameterStructures:"by-position",preferLocalImplementation:!1,preservePauseOnException:!1,idGenerator:()=>Math.random().toString(36).slice(2)}
e.AsyncCall=(e={},f)=>{let g=void 0
e instanceof Promise||(g=e),Promise.resolve(e).then(e=>g=e)
const{serializer:E,key:h,strict:v,log:b,parameterStructures:k,preferLocalImplementation:w,preservePauseOnException:$,idGenerator:P,mapError:S}={...m,...f},j=f.messageChannel,{methodNotFound:O=!1,unknownMessage:x=!1}=(e=>"boolean"!=typeof e?e:{methodNotFound:e,unknownMessage:e})(v),{beCalled:C=!0,localError:A=!0,remoteError:M=!0,type:z="pretty",sendLocalStack:N=!1}=(e=>"boolean"!=typeof e?e:{beCalled:e,localError:e,remoteError:e,type:e?"pretty":"basic"})(b),R=(e=>{const r=e||globalThis.console,t=(...e)=>r.log(...e)
return Object.assign({},{debug:t,error:t,groupCollapsed:t,groupEnd:t,log:t},r)})(f.logger),I=new Map
return j.on(h,async e=>{var r
let t,o=void 0
try{if(t=await E.deserialization(e),c(t))o=await D(t),o&&await n(o)
else if(Array.isArray(t)&&t.every(c)&&0!==t.length){const e=await Promise.all(t.map(D))
if(t.every(e=>void 0===e))return
await n(e.filter(e=>e))}else x&&await n(i.InvalidRequest(null!==(r=t.id)&&void 0!==r?r:null))}catch(e){A&&R.error(e,t,o),n(i.ParseError(e,S||s(null==e?void 0:e.stack)))}async function n(e){if(Array.isArray(e)){const r=e.filter(e=>u(e,"id"))
if(0===r.length)return
k.emit(m,await p.serialization(r))}else{if(!e)return
if(void 0===e.id)return
k.emit(m,await p.serialization(e))}}}),new Proxy({},{get(e,r){let t=o(Error().stack)
return(...e)=>{if("string"!=typeof r){if("symbol"!=typeof r)return Promise.reject(new TypeError("[AsyncCall] Only string can be the method name"))
{const e=Symbol.keyFor(r)
e&&(r=e)}}else if(r.startsWith("rpc."))return Promise.reject(new TypeError("[AsyncCall] You cannot call JSON RPC internal methods directly"))
if(h&&y&&"string"==typeof r){const t=y[r]
if(t&&"function"==typeof t)return new Promise(r=>r(t(...e)))}return new Promise((n,o)=>{const i=Math.random().toString(36).slice(2),[a]=e,s=O?t:"",c="by-name"===g&&1===e.length&&l(a)?a:e,d=((e,r,t,n)=>{const o={jsonrpc:"2.0",id:e,method:r,params:t,remoteStack:n}
return 0===n.length&&delete o.remoteStack,o})(i,r,c,s)
Promise.resolve(p.serialization(d)).then(e=>{k.emit(m,e),C.set(i,{f:[n,o],stack:t})},o)})}}})
async function M(t){if(c(t,"method"))return(async t=>{y||await e
let n=""
try{const e=t.method.startsWith("rpc.")?Symbol.for(t.method):t.method,i=y[e]
if(!i||"function"!=typeof i)return w?a.MethodNotFound(t.id):void(j&&x.debug("Receive remote call, but not implemented.",e,t))
const s=t.params
if(Array.isArray(s)||"object"==typeof s&&null!==s){const e=Array.isArray(s)?s:[s]
n=o(Error().stack)
const a=b?u(e=>n=e,i,e):new Promise(r=>r(i(...e)))
if(S)if("basic"===I)x.log(`${r.key}.${t.method}(${""+[...e]}) @${t.id}`)
else{const n=[`${r.key}.%c${t.method}%c(${e.map(()=>"%o").join(", ")}%c)\n%o %c@${t.id}`,"color: #d2c057","",...e,"",a,"color: gray; font-style: italic;"]
t.remoteStack?(x.groupCollapsed(...n),x.log(t.remoteStack),x.groupEnd()):x.log(...n)}if(await a===d)return
return((e,r,t)=>{const n={jsonrpc:"2.0",id:e,result:void 0===r?null:r}
return t||void 0!==r||(n.resultIsUndefined=!0),n})(t.id,await a,!!$)}return a.InvalidRequest(t.id)}catch(e){return"object"==typeof e&&"stack"in e&&(e.stack=n.split("\n").reduce((e,r)=>e.replace(r+"\n",""),e.stack||"")),j&&x.error(e),a(t.id,-1,null==e?void 0:e.message,null==e?void 0:e.stack,e)}})(t)
if("error"in t||"result"in t)R(t)
else{if(!("resultIsUndefined"in t))return a.InvalidRequest(t.id)
t.result=void 0,R(t)}}},e.JSONSerialization=(e=[void 0,void 0],r)=>({serialization:async t=>JSON.stringify(t,e[0],r),deserialization:async r=>JSON.parse(r,e[1])}),e.NoSerialization=r,Object.defineProperty(e,"__esModule",{value:!0})}))
j.emit(h,await E.serialization(r))}else{if(!e)return
if(!u(e,"id"))return
j.emit(h,await E.serialization(e))}}}),new Proxy({},{get(e,r){let t=o(Error().stack)
return(...e)=>{if("symbol"==typeof r){const e=Symbol.keyFor(r)
e&&(r=e)}else if(r.startsWith("rpc."))return Promise.reject(new TypeError("[AsyncCall] Can't call JSON RPC internal methods directly"))
if(w&&g&&"string"==typeof r){const t=g[r]
if(t&&"function"==typeof t)return new Promise(r=>r(t(...e)))}return new Promise((o,n)=>{const i=P(),[a]=e,s=N?t:"",c="by-name"===k&&1===e.length&&l(a)?a:e,u=((e,r,t,o)=>{const n={jsonrpc:"2.0",id:e,method:r,params:t,remoteStack:o}
return d(n,"id"),((e,r)=>{e[r]||delete e[r]})(n,"remoteStack"),n})(i,r,c,s)
Promise.resolve(E.serialization(u)).then(e=>{j.emit(h,e),I.set(i,{f:[o,n],stack:t})},n)})}}})
async function D(c){return u(c,"method")?async function(r){g||await e
let t=""
try{const e=r.method.startsWith("rpc.")?Symbol.for(r.method):r.method,n=g[e]
if("function"!=typeof n)return O?i.MethodNotFound(r.id):void(A&&R.debug("Receive remote call, but not implemented.",e,r))
const{params:a}=r,s=Array.isArray(a)?a:[a]
t=o(Error().stack)
const c=$?y(e=>t=e,n,g,s):new Promise(e=>e(n.apply(g,s)))
if(C)if("basic"===z)R.log(`${f.key}.${r.method}(${""+[...s]}) @${r.id}`)
else{const e=[`${f.key}.%c${r.method}%c(${s.map(()=>"%o").join(", ")}%c)\n%o %c@${r.id}`,"color: #d2c057","",...s,"",c,"color: gray; font-style: italic;"]
r.remoteStack?(R.groupCollapsed(...e),R.log(r.remoteStack),R.groupEnd()):R.log(...e)}if(await c===p)return
return((e,r)=>{const t={jsonrpc:"2.0",id:e,result:r}
return d(t,"id"),t})(r.id,await c)}catch(e){return"object"==typeof e&&"stack"in e&&(e.stack=t.split("\n").reduce((e,r)=>e.replace(r+"\n",""),e.stack||"")),A&&R.error(e),a(r,e,S||s(N?e.stack:void 0))}}(c):async function(e){let o="",i="",a=0,s="Error"
if(u(e,"error")){const r=e.error
o=r.message,a=r.code
const t=r.data
i=l(t)&&u(t,"stack")&&"string"==typeof t.stack?t.stack:"<remote stack not available>",s=l(t)&&u(t,"type")&&"string"==typeof t.type?t.type:"Error",M&&("basic"===z?R.error(`${s}: ${o}(${a}) @${e.id}\n${i}`):R.error(`${s}: ${o}(${a}) %c@${e.id}\n%c${i}`,"color: gray",""))}if(null===e.id||void 0===e.id)return
const{f:[c,d],stack:f}=I.get(e.id)||{stack:"",f:[null,null]}
c&&(I.delete(e.id),u(e,"error")?d(((e,o,i,a)=>{try{if(e.startsWith("DOMException:")&&n){const[,r]=e.split("DOMException:")
return new n(o,r)}if(e in t){const r=new t[e](o)
return r.stack=a,Object.assign(r,{code:i}),r}return new r(e,o,i,a)}catch(r){return Error(`E${i} ${e}: ${o}\n${a}`)}})(s,o,a,i+"\n аt AsyncCall (rpc) \n"+f)):c(e.result))}(c)}},e.JSONSerialization=(e=[void 0,void 0],r,t="null")=>({serialization(o){if(t&&l(o)&&u(o,"result")&&void 0===o.result){const e=Object.assign({},o)
e.result=null,"keep"===t&&(e.undef=!0),o=e}return JSON.stringify(o,e[0],r)},deserialization(r){const t=JSON.parse(r,e[1])
return l(t)&&u(t,"result")&&null===t.result&&u(t,"undef")&&!0===t.undef&&(t.result=void 0,delete t.undef),t}}),e.NoSerialization=f,Object.defineProperty(e,"__esModule",{value:!0})}))
//# sourceMappingURL=base.js.map

@@ -28,6 +28,14 @@ /**

* @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?: [Parameters<JSON['stringify']>[1], Parameters<JSON['parse']>[1]], space?: string | number | undefined) => Serialization;
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;

@@ -39,7 +47,7 @@ /**

interface Console {
debug(...args: unknown[]): void;
debug?(...args: unknown[]): void;
log(...args: unknown[]): void;
groupCollapsed(...args: unknown[]): void;
groupEnd(...args: unknown[]): void;
error(...args: unknown[]): void;
groupCollapsed?(...args: unknown[]): void;
groupEnd?(...args: unknown[]): void;
error?(...args: unknown[]): void;
}

@@ -84,14 +92,9 @@

/**
* Return an error when the requested method is not defined
* @defaultValue false
* Return an error when the requested method is not defined, otherwise, ignore the request.
* @defaultValue true
*/
methodNotFound?: boolean;
/**
* don't try to keep `undefined` result (then it will be `null`)
* @defaultValue false
*/
noUndefined?: boolean;
/**
* send an error when receive invalid JSON RPC payload
* @defaultValue false
* @defaultValue true
*/

@@ -101,2 +104,10 @@ unknownMessage?: boolean;

/**
* The message channel interface that allows
* @public
*/
interface MessageChannel {
on(event: string, eventListener: (data: unknown) => void): void;
emit(event: string, data: unknown): void;
}
/**
* Options for {@link AsyncCall}

@@ -123,3 +134,3 @@ * @public

*/
key: string;
key?: string;
/**

@@ -138,3 +149,3 @@ * How to serialization and deserialization JSON RPC payload

*/
serializer: Serialization;
serializer?: Serialization;
/**

@@ -146,3 +157,3 @@ * The logger of AsyncCall

*/
logger: Console;
logger?: Console;
/**

@@ -162,6 +173,3 @@ * The message channel can let you transport messages between server and client

*/
messageChannel: {
on(event: string, callback: (data: unknown) => void): void;
emit(event: string, data: unknown): void;
};
messageChannel: MessageChannel;
/**

@@ -171,3 +179,3 @@ * Choose log level. See {@link AsyncCallLogLevel}

*/
log: AsyncCallLogLevel | boolean;
log?: AsyncCallLogLevel | boolean;
/**

@@ -177,3 +185,3 @@ * Strict options. See {@link AsyncCallStrictJSONRPC}

*/
strict: AsyncCallStrictJSONRPC | boolean;
strict?: AsyncCallStrictJSONRPC | boolean;
/**

@@ -185,3 +193,3 @@ * How parameters passed to remote

*/
parameterStructures: 'by-position' | 'by-name';
parameterStructures?: 'by-position' | 'by-name';
/**

@@ -193,3 +201,3 @@ * Prefer local implementation than remote.

*/
preferLocalImplementation: boolean;
preferLocalImplementation?: boolean;
/**

@@ -206,5 +214,29 @@ * (Browser) Try to preserve the browser "pause on uncaught exception".

*/
preservePauseOnException: boolean;
preservePauseOnException?: boolean;
/**
* The ID generator of each JSON RPC request
* @defaultValue () => Math.random().toString(36).slice(2)
*/
idGenerator?(): string | number;
/**
* Control the error response data
* @param error The happened Error
* @param request The request object
*/
mapError?: ErrorMapFunction<unknown>;
}
/**
* @public
*/
declare type ErrorMapFunction<T = unknown> = (error: unknown, request: Readonly<{
jsonrpc: '2.0';
id?: string | number | null;
method: string;
params: readonly unknown[] | object;
}>) => {
code: number;
message: string;
data?: T;
};
/**
* Make all function in the type T Async

@@ -234,3 +266,3 @@ * @internal

*/
declare function AsyncCall<OtherSideImplementedFunctions = {}>(thisSideImplementation: object | Promise<object> | undefined, options: Partial<AsyncCallOptions> & Pick<AsyncCallOptions, 'messageChannel'>): _AsyncVersionOf<OtherSideImplementedFunctions>;
declare function AsyncCall<OtherSideImplementedFunctions = {}>(thisSideImplementation: object | Promise<object> | undefined, options: AsyncCallOptions): _AsyncVersionOf<OtherSideImplementedFunctions>;

@@ -289,4 +321,4 @@ /**

*/
declare function AsyncGeneratorCall<OtherSideImplementedFunctions = {}>(thisSideImplementation: object | Promise<object> | undefined, options: Partial<AsyncCallOptions> & Pick<AsyncCallOptions, 'messageChannel'>): _AsyncGeneratorVersionOf<OtherSideImplementedFunctions>;
declare function AsyncGeneratorCall<OtherSideImplementedFunctions = {}>(thisSideImplementation: object | Promise<object> | undefined, options: AsyncCallOptions): _AsyncGeneratorVersionOf<OtherSideImplementedFunctions>;
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, AsyncGeneratorCall, Console, JSONSerialization, NoSerialization, Serialization, _AsyncGeneratorVersionOf, _AsyncVersionOf, _UnboxPromise };
export { AsyncCall, AsyncCallLogLevel, AsyncCallOptions, AsyncCallStrictJSONRPC, AsyncGeneratorCall, Console, ErrorMapFunction, JSONSerialization, MessageChannel, NoSerialization, Serialization, _AsyncGeneratorVersionOf, _AsyncVersionOf, _UnboxPromise };
/// <reference types="./full.d.ts" />
((r,e)=>{"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((r=r||self).AsyncCall={})})(this,(function(r){"use strict"
const e={serialization:async r=>r,deserialization:async r=>r}
class t extends Error{constructor(r,e,t,n){super(e),this.name=r,this.code=t,this.stack=n}}const n={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError}
function o(r=""){return r.replace(/^.+\n.+\n/,"")}const i=(()=>{const r=Reflect.get(globalThis,"DOMException")
if("function"==typeof r)return r})()
function a(r,e,t,n,o){let a=((r,e)=>{let t="Error"
try{t=null===(i=null===(n=o)||void 0===n?void 0:n.constructor)||void 0===i?void 0:i.name}catch(n){}var n,i
return"string"!=typeof t?"Error":t})()
return i&&o instanceof i&&(a="DOMException:"+o.name),"string"!=typeof o&&"number"!=typeof o&&"boolean"!=typeof o&&"bigint"!=typeof o||(a="Error",t=o+""),void 0===r&&(r=null),Number.isNaN(e=Math.floor(e))&&(e=-1),{error:{code:e,message:t,data:{stack:n,type:a}},id:r,jsonrpc:"2.0"}}function s(r){if(!c(r))return!1
if(!l(r,"jsonrpc"))return!1
if("2.0"!==r.jsonrpc)return!1
if(l(r,"params")){const e=r.params
if(!Array.isArray(e)&&!c(e))return!1}return!0}function c(r){return"object"==typeof r&&null!==r}function l(r,e){return e in r}function u(){return Math.random().toString(36).slice(2)}function d(r){return"boolean"!=typeof r?r:{methodNotFound:r,unknownMessage:r,noUndefined:r}}a.ParseError=(r="")=>a(null,-32700,"Parse error",r),a.InvalidRequest=r=>a(r,-32600,"Invalid Request",""),a.MethodNotFound=r=>a(r,-32601,"Method not found",""),a.InvalidParams=r=>a(r,-32602,"Invalid params",""),a.InternalError=(r,e="")=>a(r,-32603,"Internal error"+e,"")
const f=Symbol.for("AsyncCall: This response should be ignored.")
async function y(r,e,t){const n=document.createElement("iframe"),i=new Promise((i,a)=>{try{n.style.display="none",document.body.appendChild(n)
{const s=n.contentDocument,c=n.contentWindow,l=s.createElement("button")
s.body.appendChild(l),l.onclick=()=>new c.Promise(n=>{(async()=>{r(o(Error().stack)),i(await e(...t))})().then(n)}),c.addEventListener("unhandledrejection",r=>a(r.reason)),l.click()}}catch(r){return console.error("Please close preservePauseOnException.",r),i(e(...t))}})
return i.finally(()=>n.remove()),i}const p={serializer:e,key:"default-jsonrpc",strict:!1,log:!0,parameterStructures:"by-position",preferLocalImplementation:!1,preservePauseOnException:!1}
function m(r={},e){let m=void 0
Promise.resolve(r).then(r=>m=r)
const{serializer:h,key:v,strict:g,log:E,parameterStructures:w,preferLocalImplementation:b,preservePauseOnException:_}={...p,...e},k=e.messageChannel,{methodNotFound:S=!1,noUndefined:P=!1,unknownMessage:$=!1}=d(g),{beCalled:x=!0,localError:A=!0,remoteError:j=!0,type:C="pretty",sendLocalStack:I=!1}=(r=>"boolean"!=typeof r?r:{beCalled:r,localError:r,remoteError:r,type:r?"pretty":"basic"})(E),O=(r=>{const e=r||globalThis.console,t=(...r)=>e.log(...r)
return Object.assign({},{debug:t,error:t,groupCollapsed:t,groupEnd:t,log:t},e)})(e.logger),R=new Map
async function M(r){var e,o,a
let s="",c="",u=0,d="Error"
if(l(r,"error")&&(s=r.error.message,u=r.error.code,c=null!==(o=null===(e=r.error.data)||void 0===e?void 0:e.stack)&&void 0!==o?o:"<remote stack not available>",d=(null===(a=r.error.data)||void 0===a?void 0:a.type)||"Error",j&&("basic"===C?O.error(`${d}: ${s}(${u}) @${r.id}\n${c}`):O.error(`${d}: ${s}(${u}) %c@${r.id}\n%c${c}`,"color: gray",""))),null===r.id||void 0===r.id)return
const{f:[f,y],stack:p}=R.get(r.id)||{stack:"",f:[null,null]}
f&&(R.delete(r.id),l(r,"error")?y(((r,e,o,a)=>{try{if(r.startsWith("DOMException:")&&i){const[,t]=r.split("DOMException:")
return new i(e,t)}if(r in n){const t=new n[r](e)
return t.stack=a,Object.assign(t,{code:o}),t}return new t(r,e,o,a)}catch(t){return Error(`E${o} ${r}: ${e}\n${a}`)}})(d,s,u,c+"\n аt AsyncCall (rpc) \n"+p)):f(r.result))}return k.on(v,async r=>{let e,t=void 0
try{if(e=await h.deserialization(r),s(e))t=await N(e),t&&await n(t)
else if(Array.isArray(e)&&e.every(s)&&0!==e.length){const r=await Promise.all(e.map(N))
if(e.every(r=>void 0===r))return
await n(r.filter(r=>r))}else $&&await n(a.InvalidRequest(e.id||null))}catch(r){O.error(r,e,t),n(a.ParseError(null==r?void 0:r.stack))}async function n(r){if(Array.isArray(r)){const e=r.map(r=>r).filter(r=>void 0!==r.id)
if(0===e.length)return
k.emit(v,await h.serialization(e))}else{if(!r)return
if(void 0===r.id)return
k.emit(v,await h.serialization(r))}}}),new Proxy({},{get(r,e){let t=o(Error().stack)
return(...r)=>{if("string"!=typeof e){if("symbol"!=typeof e)return Promise.reject(new TypeError("[AsyncCall] Only string can be the method name"))
{const r=Symbol.keyFor(e)
r&&(e=r)}}else if(e.startsWith("rpc."))return Promise.reject(new TypeError("[AsyncCall] You cannot call JSON RPC internal methods directly"))
if(b&&m&&"string"==typeof e){const t=m[e]
if(t&&"function"==typeof t)return new Promise(e=>e(t(...r)))}return new Promise((n,o)=>{const i=u(),[a]=r,s=I?t:"",l="by-name"===w&&1===r.length&&c(a)?a:r,d=((r,e,t,n)=>{const o={jsonrpc:"2.0",id:r,method:e,params:t,remoteStack:n}
return 0===n.length&&delete o.remoteStack,o})(i,e,l,s)
Promise.resolve(h.serialization(d)).then(r=>{k.emit(v,r),R.set(i,{f:[n,o],stack:t})},o)})}}})
async function N(t){if(l(t,"method"))return(async t=>{m||await r
let n=""
try{const r=t.method.startsWith("rpc.")?Symbol.for(t.method):t.method,i=m[r]
if(!i||"function"!=typeof i)return S?a.MethodNotFound(t.id):void(A&&O.debug("Receive remote call, but not implemented.",r,t))
const s=t.params
if(Array.isArray(s)||"object"==typeof s&&null!==s){const r=Array.isArray(s)?s:[s]
n=o(Error().stack)
const a=_?y(r=>n=r,i,r):new Promise(e=>e(i(...r)))
if(x)if("basic"===C)O.log(`${e.key}.${t.method}(${""+[...r]}) @${t.id}`)
else{const n=[`${e.key}.%c${t.method}%c(${r.map(()=>"%o").join(", ")}%c)\n%o %c@${t.id}`,"color: #d2c057","",...r,"",a,"color: gray; font-style: italic;"]
t.remoteStack?(O.groupCollapsed(...n),O.log(t.remoteStack),O.groupEnd()):O.log(...n)}if(await a===f)return
return((r,e,t)=>{const n={jsonrpc:"2.0",id:r,result:void 0===e?null:e}
return t||void 0!==e||(n.resultIsUndefined=!0),n})(t.id,await a,!!P)}return a.InvalidRequest(t.id)}catch(r){return"object"==typeof r&&"stack"in r&&(r.stack=n.split("\n").reduce((r,e)=>r.replace(e+"\n",""),r.stack||"")),A&&O.error(r),a(t.id,-1,null==r?void 0:r.message,null==r?void 0:r.stack,r)}})(t)
if("error"in t||"result"in t)M(t)
else{if(!("resultIsUndefined"in t))return a.InvalidRequest(t.id)
t.result=void 0,M(t)}}}const h=Symbol.for("rpc.async-iterator.start"),v=Symbol.for("rpc.async-iterator.next"),g=Symbol.for("rpc.async-iterator.return"),E=Symbol.for("rpc.async-iterator.throw")
class w{constructor(r,e){this.__3=r=>(b(r),r),this.__0=r,this.__1=e,this.__2=!1}async return(r){return this.__2?_(!0,r):this.__3(this.__0[g](await this.__1,r))}async next(r){return this.__2?_(!0):this.__3(this.__0[v](await this.__1,r))}async throw(r){if(this.__2)throw r
return this.__3(this.__0[E](await this.__1,r))}[Symbol.asyncIterator](){return this}}async function b(r,e){const t=await r
null==t||t.done}function _(r,e){return{done:r,value:e}}r.AsyncCall=m,r.AsyncGeneratorCall=(r={},e)=>{const t=new Map,n=d(e.strict||!1)
function o(r,e,o){const i=t.get(r)
if(!i){if(n.methodNotFound)throw Error("Remote iterator not found while executing "+e)
return f}const a=o(i)
return b(a),a}const i=m({async[h](e,o){const i=Reflect.get(await r,e)
if("function"!=typeof i){if(n.methodNotFound)throw Error(e+" is not a function")
return f}const a=i(...o),s=u()
return t.set(s,a),Promise.resolve(s)},[v]:(r,e)=>o(r,"next",r=>r.next(e)),[g]:(r,e)=>o(r,"return",r=>{var t
return null===(t=r.return)||void 0===t?void 0:t.call(r,e)}),[E]:(r,e)=>o(r,"throw",r=>{var t
return null===(t=r.throw)||void 0===t?void 0:t.call(r,e)})},e)
return new Proxy({},{get:(r,e)=>{if("string"!=typeof e)throw new TypeError("[*AsyncCall] Only string can be the method name")
return(...r)=>{const t=i[h](e,r)
return new w(i,t)}}})},r.JSONSerialization=(r=[void 0,void 0],e)=>({serialization:async t=>JSON.stringify(t,r[0],e),deserialization:async e=>JSON.parse(e,r[1])}),r.NoSerialization=e,Object.defineProperty(r,"__esModule",{value:!0})}))
((e,r)=>{"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((e=e||self).AsyncCall={})})(this,(function(e){"use strict"
class r extends Error{constructor(e,r,t,o){super(r),this.name=e,this.code=t,this.stack=o}}const t={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError}
function o(e=""){return e.replace(/^.+\n.+\n/,"")}const n=(()=>{const e=Reflect.get(globalThis,"DOMException")
if("function"==typeof e)return e})()
function i(e,r,t,o){void 0===e&&(e=null),Number.isNaN(r=Math.floor(r))&&(r=-1)
const n={jsonrpc:"2.0",id:e,error:{code:r,message:t,data:o}}
return d(n.error,"data"),n}function a(e,r,t){const{id:o}=e,{code:n,message:a,data:s}=t(r,e)
return i(o,n,a,s)}i.ParseError=(e,r)=>{const t=a({},e,r),o=t.error
return o.code=-32700,o.message="Parse error",t},i.InvalidRequest=e=>i(e,-32600,"Invalid Request"),i.MethodNotFound=e=>i(e,-32601,"Method not found")
const s=(e="",r=-1)=>t=>{let o=""
l(t)&&u(t,"message")&&"string"==typeof t.message&&(o=t.message)
let i=((e,r)=>{let o="Error"
try{o=null===(i=null===(n=t)||void 0===n?void 0:n.constructor)||void 0===i?void 0:i.name}catch(n){}var n,i
return"string"!=typeof o?"Error":o})()
return n&&t instanceof n&&(i="DOMException:"+t.name),"string"!=typeof t&&"number"!=typeof t&&"boolean"!=typeof t&&"bigint"!=typeof t||(i="Error",o=t+""),{code:r,message:o,data:e?{stack:e,type:i}:{type:i}}}
function c(e){if(!l(e))return!1
if(!u(e,"jsonrpc"))return!1
if("2.0"!==e.jsonrpc)return!1
if(u(e,"params")){const r=e.params
if(!Array.isArray(r)&&!l(r))return!1}return!0}function l(e){return"object"==typeof e&&null!==e}function u(e,r){return r in e}function d(e,r){void 0===e[r]&&delete e[r]}const f={serialization:e=>e,deserialization:e=>e}
function y(){return Math.random().toString(36).slice(2)}function p(e){return"boolean"!=typeof e?e:{methodNotFound:e,unknownMessage:e}}const m=Symbol.for("AsyncCall/ignored")
async function h(e,r,t,n){return new Promise((i,a)=>{var s
let c={}
try{c=document.createElement("iframe"),c.style.display="none",document.body.appendChild(c)
{const s=c.contentDocument,l=c.contentWindow,u=s.createElement("button")
s.body.appendChild(u),u.onclick=()=>new l.Promise(a=>{(async()=>{e(o(Error().stack)),i(await r.apply(t,n))})().then(a)}),l.onunhandledrejection=e=>a(e.reason),u.click()}}catch(e){try{console.error("Please close preservePauseOnException.",e)}catch(e){}return i(r(...n))}finally{null===(s=null==c?void 0:c.remove)||void 0===s||s.call(c)}})}const g={serializer:f,key:"default-jsonrpc",strict:!0,log:!0,parameterStructures:"by-position",preferLocalImplementation:!1,preservePauseOnException:!1,idGenerator:y}
function v(e={},f){let y=void 0
e instanceof Promise||(y=e),Promise.resolve(e).then(e=>y=e)
const{serializer:v,key:E,strict:w,log:b,parameterStructures:k,preferLocalImplementation:P,preservePauseOnException:$,idGenerator:S,mapError:O}={...g,...f},j=f.messageChannel,{methodNotFound:x=!1,unknownMessage:C=!1}=p(w),{beCalled:A=!0,localError:M=!0,remoteError:N=!0,type:R="pretty",sendLocalStack:z=!1}=(e=>"boolean"!=typeof e?e:{beCalled:e,localError:e,remoteError:e,type:e?"pretty":"basic"})(b),I=(e=>{const r=e||globalThis.console,t=(...e)=>r.log(...e)
return Object.assign({},{debug:t,error:t,groupCollapsed:t,groupEnd:t,log:t},r)})(f.logger),F=new Map
return j.on(E,async e=>{var r
let t,o=void 0
try{if(t=await v.deserialization(e),c(t))o=await T(t),o&&await n(o)
else if(Array.isArray(t)&&t.every(c)&&0!==t.length){const e=await Promise.all(t.map(T))
if(t.every(e=>void 0===e))return
await n(e.filter(e=>e))}else C&&await n(i.InvalidRequest(null!==(r=t.id)&&void 0!==r?r:null))}catch(e){M&&I.error(e,t,o),n(i.ParseError(e,O||s(null==e?void 0:e.stack)))}async function n(e){if(Array.isArray(e)){const r=e.filter(e=>u(e,"id"))
if(0===r.length)return
j.emit(E,await v.serialization(r))}else{if(!e)return
if(!u(e,"id"))return
j.emit(E,await v.serialization(e))}}}),new Proxy({},{get(e,r){let t=o(Error().stack)
return(...e)=>{if("symbol"==typeof r){const e=Symbol.keyFor(r)
e&&(r=e)}else if(r.startsWith("rpc."))return Promise.reject(new TypeError("[AsyncCall] Can't call JSON RPC internal methods directly"))
if(P&&y&&"string"==typeof r){const t=y[r]
if(t&&"function"==typeof t)return new Promise(r=>r(t(...e)))}return new Promise((o,n)=>{const i=S(),[a]=e,s=z?t:"",c="by-name"===k&&1===e.length&&l(a)?a:e,u=((e,r,t,o)=>{const n={jsonrpc:"2.0",id:e,method:r,params:t,remoteStack:o}
return d(n,"id"),((e,r)=>{e[r]||delete e[r]})(n,"remoteStack"),n})(i,r,c,s)
Promise.resolve(v.serialization(u)).then(e=>{j.emit(E,e),F.set(i,{f:[o,n],stack:t})},n)})}}})
async function T(c){return u(c,"method")?async function(r){y||await e
let t=""
try{const e=r.method.startsWith("rpc.")?Symbol.for(r.method):r.method,n=y[e]
if("function"!=typeof n)return x?i.MethodNotFound(r.id):void(M&&I.debug("Receive remote call, but not implemented.",e,r))
const{params:a}=r,s=Array.isArray(a)?a:[a]
t=o(Error().stack)
const c=$?h(e=>t=e,n,y,s):new Promise(e=>e(n.apply(y,s)))
if(A)if("basic"===R)I.log(`${f.key}.${r.method}(${""+[...s]}) @${r.id}`)
else{const e=[`${f.key}.%c${r.method}%c(${s.map(()=>"%o").join(", ")}%c)\n%o %c@${r.id}`,"color: #d2c057","",...s,"",c,"color: gray; font-style: italic;"]
r.remoteStack?(I.groupCollapsed(...e),I.log(r.remoteStack),I.groupEnd()):I.log(...e)}if(await c===m)return
return((e,r)=>{const t={jsonrpc:"2.0",id:e,result:r}
return d(t,"id"),t})(r.id,await c)}catch(e){return"object"==typeof e&&"stack"in e&&(e.stack=t.split("\n").reduce((e,r)=>e.replace(r+"\n",""),e.stack||"")),M&&I.error(e),a(r,e,O||s(z?e.stack:void 0))}}(c):async function(e){let o="",i="",a=0,s="Error"
if(u(e,"error")){const r=e.error
o=r.message,a=r.code
const t=r.data
i=l(t)&&u(t,"stack")&&"string"==typeof t.stack?t.stack:"<remote stack not available>",s=l(t)&&u(t,"type")&&"string"==typeof t.type?t.type:"Error",N&&("basic"===R?I.error(`${s}: ${o}(${a}) @${e.id}\n${i}`):I.error(`${s}: ${o}(${a}) %c@${e.id}\n%c${i}`,"color: gray",""))}if(null===e.id||void 0===e.id)return
const{f:[c,d],stack:f}=F.get(e.id)||{stack:"",f:[null,null]}
c&&(F.delete(e.id),u(e,"error")?d(((e,o,i,a)=>{try{if(e.startsWith("DOMException:")&&n){const[,r]=e.split("DOMException:")
return new n(o,r)}if(e in t){const r=new t[e](o)
return r.stack=a,Object.assign(r,{code:i}),r}return new r(e,o,i,a)}catch(r){return Error(`E${i} ${e}: ${o}\n${a}`)}})(s,o,a,i+"\n аt AsyncCall (rpc) \n"+f)):c(e.result))}(c)}}const E=Symbol.for("rpc.async-iterator.start"),w=Symbol.for("rpc.async-iterator.next"),b=Symbol.for("rpc.async-iterator.return"),k=Symbol.for("rpc.async-iterator.throw")
class P{constructor(e,r){this.r=e,this.i=r,this.d=!1,this.c=async e=>(await O(e,()=>this.d=!0),e)}async return(e){return this.d||this.c(this.r[b](await this.i,e)).catch(()=>{}),this.d=!0,j(!0,e)}async next(e){return this.d?j(!0):await this.c(this.r[w](await this.i,e))}async throw(e){if(!this.d)return await this.c(this.r[k](await this.i,e))
throw e}}const $=async function*(){}.constructor.prototype
Object.setPrototypeOf(P,$)
const S=Object.getPrototypeOf(async function*(){}())
async function O(e,r){try{const t=await e;(null==t?void 0:t.done)&&r()}catch(e){}}function j(e,r){return{done:e,value:r}}Object.setPrototypeOf(P.prototype,S),e.AsyncCall=v,e.AsyncGeneratorCall=(e={},r)=>{var t
const o=new Map,n=p(null===(t=r.strict)||void 0===t||t),{idGenerator:i=y}=r
function a(e,r,t){const i=o.get(e)
if(!i){if(n.methodNotFound)throw Error(`Iterator ${e} not found, ${r}() failed.`)
return m}const a=t(i)
return O(a,()=>o.delete(e)),a}const s=v({async[E](r,t){const a=Reflect.get(await e,r)
if("function"!=typeof a){if(n.methodNotFound)throw Error(r+" is not a function")
return m}const s=a(...t),c=i()
return o.set(c,s),Promise.resolve(c)},[w]:(e,r)=>a(e,"next",e=>e.next(r)),[b]:(e,r)=>a(e,"return",e=>{var t
return null===(t=e.return)||void 0===t?void 0:t.call(e,r)}),[k]:(e,r)=>a(e,"throw",e=>{var t
return null===(t=e.throw)||void 0===t?void 0:t.call(e,r)})},r)
return new Proxy({},{get:(e,r)=>{if("string"!=typeof r)throw new TypeError("[*AsyncCall] Only string can be the method name")
return(...e)=>{const t=s[E](r,e)
return new P(s,t)}}})},e.JSONSerialization=(e=[void 0,void 0],r,t="null")=>({serialization(o){if(t&&l(o)&&u(o,"result")&&void 0===o.result){const e=Object.assign({},o)
e.result=null,"keep"===t&&(e.undef=!0),o=e}return JSON.stringify(o,e[0],r)},deserialization(r){const t=JSON.parse(r,e[1])
return l(t)&&u(t,"result")&&null===t.result&&u(t,"undef")&&!0===t.undef&&(t.result=void 0,delete t.undef),t}}),e.NoSerialization=f,Object.defineProperty(e,"__esModule",{value:!0})}))
//# sourceMappingURL=full.js.map
{
"name": "async-call-rpc",
"version": "2.0.2",
"version": "3.0.0",
"description": "A lightweight JSON RPC server & client",
"main": "out/full.js",
"module": "out/full.mjs",
"types": "./out/full.d.ts",
"main": "out/base.js",
"module": "out/base.mjs",
"types": "./out/base.d.ts",
"exports": {
".": {
"require": "./out/base.js",
"import": "./out/base.mjs"
},
"./full": {
"require": "./out/full.js",

@@ -19,11 +23,13 @@ "import": "./out/full.mjs"

"scripts": {
"start": "node ./watch.js",
"prepublishOnly": "npm run build",
"release": "rimraf ./out && standard-version",
"start": "node ./watch.js",
"build": "tsc --declaration --outDir ./es/",
"prepublishOnly": "npm run build && npm run roll && npm run doc",
"build:tsc": "tsc --declaration --outDir ./es/",
"build:roll": "rollup -c",
"build": "npm run build:tsc && npm run build:roll && npm run doc",
"doc:api": "api-extractor run --local --verbose",
"doc:md": "api-documenter markdown -o docs -i temp",
"doc:preview": "docsify serve .",
"doc": "npm run doc:api && npm run doc:md",
"doc:preview": "docsify serve .",
"roll": "rollup -c"
"test": "jest --coverage --coverage-provider=v8"
},

@@ -47,8 +53,13 @@ "repository": {

"@rollup/plugin-typescript": "^4.1.2",
"@types/jest": "^26.0.0",
"@types/node": "^14.0.13",
"docsify-cli": "^4.4.1",
"jest": "^26.0.1",
"prettier": "^2.0.5",
"rimraf": "^3.0.2",
"rollup": "^2.13.1",
"rollup": "^2.16.1",
"rollup-plugin-dts": "^1.4.7",
"rollup-plugin-terser": "^6.1.0",
"standard-version": "^8.0.0",
"ts-jest": "^26.1.0",
"tslib": "^2.0.0",

@@ -55,0 +66,0 @@ "typescript": "^3.9.5"

@@ -5,5 +5,10 @@ # Async Call

[![Code coverage](https://codecov.io/gh/Jack-Works/async-call-rpc/branch/master/graph/badge.svg)](https://codecov.io/gh/Jack-Works/async-call-rpc)
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/Jack-Works/async-call-rpc/build)](https://github.com/Jack-Works/async-call-rpc/actions)
[![npm](https://img.shields.io/npm/v/async-call-rpc)](https://npmjs.org/async-call-rpc)
![ES2015+](https://img.shields.io/badge/ECMAScript-2015%2B-brightgreen)
## Links
[CHANGELOG.md](./CHANGELOG.md) | [Document of AsyncCall](https://jack-works.github.io/async-call/async-call-rpc.asynccall.html) | [Document of AsyncGeneratorCall](https://jack-works.github.io/async-call/async-call-rpc.asyncgeneratorcall.html) | [Playground](https://jack-works.github.io/async-call/)
[CHANGELOG.md](./CHANGELOG.md) | [Document of AsyncCall](https://jack-works.github.io/async-call-rpc/async-call-rpc.asynccall.html) | [Document of AsyncGeneratorCall](https://jack-works.github.io/async-call-rpc/async-call-rpc.asyncgeneratorcall.html) | [Playground](https://jack-works.github.io/async-call-rpc/)

@@ -16,2 +21,3 @@ Chapters:

- [Entries](#entries)
- [Look this if both server and client are created by this library](#look-this-if-both-server-and-client-are-created-by-this-library)
- [Implemented JSON RPC internal methods](#implemented-json-rpc-internal-methods)

@@ -32,4 +38,3 @@ - [Non-standard extension to JSON RPC specification](#non-standard-extension-to-json-rpc-specification)

- NOT support ECMAScript 5 (ES6 `Proxy` is the core of this library)
- This package is shipping ECMAScript 2018 syntax (including `async function`). You need to use a transformer to transpile to ES6.
- The default configuration is not standard JSON RPC (with a small extension to help easy using in JavaScript). But you can [switch on the "strict" mode](https://jack-works.github.io/async-call/async-call-rpc.asynccallstrictjsonrpc.html)
- This package is shipping ECMAScript 2018 syntax (including `async function`).
- The async generator mode might leak memory on the server. Use it by your caution.

@@ -131,6 +136,4 @@ - NOT support JSON RPC 1.0

> Currently the default entry is `full` but in the next major version, it will be `base`.
This library has 2 entry. `base` and `full`. `base` is the default entry point. The `full` version includes the `AsyncGeneratorCall` but the base version doesn't.
This library has 2 entry. `base` and `full`. The difference is the `full` version includes the `AsyncGeneratorCall` but the base version doesn't.
### Browser / Deno

@@ -152,2 +155,9 @@

## Look this if both server and client are created by this library
AsyncCall has some non-standard extensions to the JSON RPC specification that can help the library easier to use. Those features aren't enabled by default.
- Send call stack of Error response or send call stack of caller's request. See [remoteStack on Request object](#remotestack-on-request-object)
- Try to keep the "undefined" result when using JSONSerialization. See ["undef" on response object](#undef-on-response-object)
## Implemented JSON RPC internal methods

@@ -177,3 +187,3 @@

Controlled by [`option.log.sendLocalStack`](https://jack-works.github.io/async-call/async-call-rpc.asynccallloglevel.sendlocalstack.html). Default to `false`.
Controlled by [`option.log.sendLocalStack`](https://jack-works.github.io/async-call-rpc/async-call-rpc.asynccallloglevel.sendlocalstack.html). Default to `false`.

@@ -187,7 +197,7 @@ ```ts

### resultIsUndefined on Response object
### "undef" on Response object
This is a non-standard property. It's a hint to the client, that the result is `undefined` (because in JSON there is no `undefined`, only `null`). If this is option is off, the `undefined` result will become `null`.
This is a non-standard property appears when using JSONSerialization due to JSON doesn't support `undefined`. It's a hint to the client, that the result is `undefined`.
Controlled by [`strict.noUndefined`](https://jack-works.github.io/async-call/async-call-rpc.asynccallstrictjsonrpc.noundefined.html). Default to `false`.
This behavior is controlled by the 3rd parameter of [JSONSerialization(replacerAndReceiver?, space?, undefinedKeepingBehavior?: false | "keep" | "null" = "null")](https://jack-works.github.io/async-call-rpc/async-call-rpc.jsonserialization.html). Default to `"null"`. To turn on this feature to "keep" undefined values, change the 3rd option to "keep".

@@ -198,3 +208,3 @@ ```ts

// If the client is run in JavaScript, it should treat "result: null" as "result: undefined"
resultIsUndefined?: boolean
undef?: boolean
}

@@ -209,4 +219,2 @@ ```

Todo: [Disable this behavior](https://github.com/Jack-Works/async-call/issues/18)
```ts

@@ -213,0 +221,0 @@ interface JSONRPC_Error_object {

/**
* See the document at https://github.com/Jack-Works/async-call/
*/
import { AsyncCallOptions, AsyncCall } from './Async-Call.js'
import { AsyncCallOptions, AsyncCall } from './Async-Call'
import { AsyncCallIgnoreResponse } from './utils/internalSymbol'

@@ -9,12 +9,12 @@ import { normalizeStrictOptions } from './utils/normalizeOptions'

const _AsyncIteratorStart = Symbol.for('rpc.async-iterator.start')
const _AsyncIteratorNext = Symbol.for('rpc.async-iterator.next')
const _AsyncIteratorReturn = Symbol.for('rpc.async-iterator.return')
const _AsyncIteratorThrow = Symbol.for('rpc.async-iterator.throw')
const AsyncIteratorStart = Symbol.for('rpc.async-iterator.start')
const AsyncIteratorNext = Symbol.for('rpc.async-iterator.next')
const AsyncIteratorReturn = Symbol.for('rpc.async-iterator.return')
const AsyncIteratorThrow = Symbol.for('rpc.async-iterator.throw')
interface AsyncGeneratorInternalMethods {
[_AsyncIteratorStart](method: string, params: unknown[]): Promise<string>
[_AsyncIteratorNext](id: string, value: unknown): Promise<IteratorResult<unknown>>
[_AsyncIteratorReturn](id: string, value: unknown): Promise<IteratorResult<unknown>>
[_AsyncIteratorThrow](id: string, value: unknown): Promise<IteratorResult<unknown>>
[AsyncIteratorStart](method: string, params: unknown[]): Promise<string>
[AsyncIteratorNext](id: string, value: unknown): Promise<IteratorResult<unknown>>
[AsyncIteratorReturn](id: string, value: unknown): Promise<IteratorResult<unknown>>
[AsyncIteratorThrow](id: string, value: unknown): Promise<IteratorResult<unknown>>
}

@@ -85,6 +85,7 @@ /**

thisSideImplementation: object | Promise<object> = {},
options: Partial<AsyncCallOptions> & Pick<AsyncCallOptions, 'messageChannel'>,
options: AsyncCallOptions,
): _AsyncGeneratorVersionOf<OtherSideImplementedFunctions> {
const iterators = new Map<string, Iter>()
const strict = normalizeStrictOptions(options.strict || false)
const iterators = new Map<string | number, Iter>()
const strict = normalizeStrictOptions(options.strict ?? true)
const { idGenerator = generateRandomID } = options
function findIterator(

@@ -97,3 +98,3 @@ id: string,

if (!it) {
if (strict.methodNotFound) throw new Error(`Remote iterator not found while executing ${label}`)
if (strict.methodNotFound) throw new Error(`Iterator ${id} not found, ${label}() failed.`)
else return AsyncCallIgnoreResponse

@@ -106,3 +107,3 @@ }

const server = {
async [_AsyncIteratorStart](method, args) {
async [AsyncIteratorStart](method, args) {
const iteratorGenerator: unknown = Reflect.get(await thisSideImplementation, method)

@@ -114,13 +115,13 @@ if (typeof iteratorGenerator !== 'function') {

const iterator = iteratorGenerator(...args)
const id = generateRandomID()
const id = idGenerator()
iterators.set(id, iterator)
return Promise.resolve(id)
},
[_AsyncIteratorNext](id, val) {
[AsyncIteratorNext](id, val) {
return findIterator(id, 'next', (it) => it.next(val as any))
},
[_AsyncIteratorReturn](id, val) {
[AsyncIteratorReturn](id, val) {
return findIterator(id, 'return', (it) => it.return?.(val))
},
[_AsyncIteratorThrow](id, val) {
[AsyncIteratorThrow](id, val) {
return findIterator(id, 'throw', (it) => it.throw?.(val))

@@ -136,3 +137,3 @@ },

return function (...args: unknown[]) {
const id = remote[_AsyncIteratorStart](key, args)
const id = remote[AsyncIteratorStart](key, args)
return new AsyncGenerator(remote, id)

@@ -144,37 +145,41 @@ }

class AsyncGenerator implements AsyncIterableIterator<unknown>, AsyncIterator<unknown, unknown, unknown> {
// #remoteImpl
private __0: AsyncGeneratorInternalMethods
// #id
private __1: Promise<string>
// #done
private __2: boolean
// #check
private __3 = (val: IterResult) => {
isFinished(val, () => (this.__2 = true))
/** done? */
private d: boolean = false
/** check */
private c = async (val: IterResult) => {
await isFinished(val, () => (this.d = true))
return val
}
constructor(remoteImpl: AsyncGeneratorInternalMethods, id: Promise<string>) {
this.__0 = remoteImpl
this.__1 = id
this.__2 = false
}
/**
* @param r Remote Implementation
* @param i id
*/
constructor(private r: AsyncGeneratorInternalMethods, private i: Promise<string>) {}
async return(val: unknown) {
if (this.__2) return makeIteratorResult(true, val)
return this.__3(this.__0[_AsyncIteratorReturn](await this.__1, val))
if (!this.d) this.c(this.r[AsyncIteratorReturn](await this.i, val)).catch(() => {})
this.d = true
return makeIteratorResult(true, val)
}
async next(val?: unknown) {
if (this.__2) return makeIteratorResult(true)
return this.__3(this.__0[_AsyncIteratorNext](await this.__1, val))
if (this.d) return makeIteratorResult(true)
return await this.c(this.r[AsyncIteratorNext](await this.i, val))
}
async throw(val?: unknown) {
if (this.__2) throw val
return this.__3(this.__0[_AsyncIteratorThrow](await this.__1, val))
if (!this.d) return await this.c(this.r[AsyncIteratorThrow](await this.i, val))
throw val
}
[Symbol.asyncIterator]() {
return this
}
// Inherited from AsyncGeneratorPrototype
declare [Symbol.asyncIterator]: () => this
}
const AsyncGeneratorConstructor = async function* () {}.constructor
const AsyncGeneratorConstructorPrototype = AsyncGeneratorConstructor.prototype
Object.setPrototypeOf(AsyncGenerator, AsyncGeneratorConstructorPrototype)
const AsyncGeneratorPrototype = Object.getPrototypeOf((async function* () {})())
Object.setPrototypeOf(AsyncGenerator.prototype, AsyncGeneratorPrototype)
async function isFinished(result: IterResult | undefined, cb: () => void) {
const x = await result
!!x?.done && cb
try {
const x = await result
!!x?.done && cb()
} catch {}
}

@@ -181,0 +186,0 @@

@@ -9,3 +9,13 @@ /**

export { Console } from './utils/console'
import { Request, Response, ErrorResponse, SuccessResponse, hasKey, isJSONRPCObject, isObject } from './utils/jsonrpc'
import {
Request,
Response,
ErrorResponseMapped,
SuccessResponse,
hasKey,
isJSONRPCObject,
isObject,
ErrorResponse,
defaultErrorMapper,
} from './utils/jsonrpc'
import { removeStackHeader, RecoverError } from './utils/error'

@@ -55,14 +65,9 @@ import { generateRandomID } from './utils/generateRandomID'

/**
* Return an error when the requested method is not defined
* @defaultValue false
* Return an error when the requested method is not defined, otherwise, ignore the request.
* @defaultValue true
*/
methodNotFound?: boolean
/**
* don't try to keep `undefined` result (then it will be `null`)
* @defaultValue false
*/
noUndefined?: boolean
/**
* send an error when receive invalid JSON RPC payload
* @defaultValue false
* @defaultValue true
*/

@@ -73,2 +78,11 @@ unknownMessage?: boolean

/**
* The message channel interface that allows
* @public
*/
export interface MessageChannel {
on(event: string, eventListener: (data: unknown) => void): void
emit(event: string, data: unknown): void
}
/**
* Options for {@link AsyncCall}

@@ -95,3 +109,3 @@ * @public

*/
key: string
key?: string
/**

@@ -110,3 +124,3 @@ * How to serialization and deserialization JSON RPC payload

*/
serializer: Serialization
serializer?: Serialization
/**

@@ -118,3 +132,3 @@ * The logger of AsyncCall

*/
logger: Console
logger?: Console
/**

@@ -134,6 +148,3 @@ * The message channel can let you transport messages between server and client

*/
messageChannel: {
on(event: string, callback: (data: unknown) => void): void
emit(event: string, data: unknown): void
}
messageChannel: MessageChannel
/**

@@ -143,3 +154,3 @@ * Choose log level. See {@link AsyncCallLogLevel}

*/
log: AsyncCallLogLevel | boolean
log?: AsyncCallLogLevel | boolean
/**

@@ -149,3 +160,3 @@ * Strict options. See {@link AsyncCallStrictJSONRPC}

*/
strict: AsyncCallStrictJSONRPC | boolean
strict?: AsyncCallStrictJSONRPC | boolean
/**

@@ -157,3 +168,3 @@ * How parameters passed to remote

*/
parameterStructures: 'by-position' | 'by-name'
parameterStructures?: 'by-position' | 'by-name'
/**

@@ -165,3 +176,3 @@ * Prefer local implementation than remote.

*/
preferLocalImplementation: boolean
preferLocalImplementation?: boolean
/**

@@ -178,6 +189,34 @@ * (Browser) Try to preserve the browser "pause on uncaught exception".

*/
preservePauseOnException: boolean
preservePauseOnException?: boolean
/**
* The ID generator of each JSON RPC request
* @defaultValue () => Math.random().toString(36).slice(2)
*/
idGenerator?(): string | number
/**
* Control the error response data
* @param error The happened Error
* @param request The request object
*/
mapError?: ErrorMapFunction<unknown>
}
/**
* @public
*/
export type ErrorMapFunction<T = unknown> = (
error: unknown,
request: Readonly<{
jsonrpc: '2.0'
id?: string | number | null
method: string
params: readonly unknown[] | object
}>,
) => {
code: number
message: string
data?: T
}
/**
* Make all function in the type T Async

@@ -194,6 +233,8 @@ * @internal

const AsyncCallDefaultOptions = (<T extends Partial<AsyncCallOptions>>(a: T) => a)({
const AsyncCallDefaultOptions = (<T extends Omit<Required<AsyncCallOptions>, 'messageChannel' | 'logger' | 'mapError'>>(
a: T,
) => a)({
serializer: NoSerialization,
key: 'default-jsonrpc',
strict: false,
strict: true,
log: true,

@@ -203,3 +244,4 @@ parameterStructures: 'by-position',

preservePauseOnException: false,
} as const)
idGenerator: generateRandomID,
})

@@ -226,7 +268,18 @@ /**

thisSideImplementation: object | Promise<object> = {},
options: Partial<AsyncCallOptions> & Pick<AsyncCallOptions, 'messageChannel'>,
options: AsyncCallOptions,
): _AsyncVersionOf<OtherSideImplementedFunctions> {
let resolvedThisSideImplementation: object | undefined = undefined
if (!(thisSideImplementation instanceof Promise)) resolvedThisSideImplementation = thisSideImplementation
Promise.resolve(thisSideImplementation).then((x) => (resolvedThisSideImplementation = x))
const { serializer, key, strict, log, parameterStructures, preferLocalImplementation, preservePauseOnException } = {
const {
serializer,
key,
strict,
log,
parameterStructures,
preferLocalImplementation,
preservePauseOnException,
idGenerator,
mapError,
} = {
...AsyncCallDefaultOptions,

@@ -238,3 +291,2 @@ ...options,

methodNotFound: banMethodNotFound = false,
noUndefined: noUndefinedKeeping = false,
unknownMessage: banUnknownMessage = false,

@@ -259,3 +311,3 @@ } = normalizeStrictOptions(strict)

const executor: unknown = resolvedThisSideImplementation![key]
if (!executor || typeof executor !== 'function') {
if (typeof executor !== 'function') {
if (!banMethodNotFound) {

@@ -266,35 +318,36 @@ if (logLocalError) console.debug('Receive remote call, but not implemented.', key, data)

}
const params: unknown = data.params
if (Array.isArray(params) || (typeof params === 'object' && params !== null)) {
const args = Array.isArray(params) ? params : [params]
frameworkStack = removeStackHeader(new Error().stack)
const promise = preservePauseOnException
? preservePauseOnExceptionCaller((x) => (frameworkStack = x), executor, args)
: new Promise((resolve) => resolve(executor(...args)))
if (logBeCalled) {
if (logType === 'basic')
console.log(`${options.key}.${data.method}(${[...args].toString()}) @${data.id}`)
else {
const logArgs = [
`${options.key}.%c${data.method}%c(${args.map(() => '%o').join(', ')}%c)\n%o %c@${data.id}`,
'color: #d2c057',
'',
...args,
'',
promise,
'color: gray; font-style: italic;',
]
if (data.remoteStack) {
console.groupCollapsed(...logArgs)
console.log(data.remoteStack)
console.groupEnd()
} else console.log(...logArgs)
}
const { params } = data
const args = Array.isArray(params) ? params : [params]
frameworkStack = removeStackHeader(new Error().stack)
const promise = preservePauseOnException
? preservePauseOnExceptionCaller(
(x) => (frameworkStack = x),
executor,
resolvedThisSideImplementation,
args,
)
: new Promise((resolve) => resolve(executor.apply(resolvedThisSideImplementation, args)))
if (logBeCalled) {
if (logType === 'basic')
console.log(`${options.key}.${data.method}(${[...args].toString()}) @${data.id}`)
else {
const logArgs = [
`${options.key}.%c${data.method}%c(${args.map(() => '%o').join(', ')}%c)\n%o %c@${data.id}`,
'color: #d2c057',
'',
...args,
'',
promise,
'color: gray; font-style: italic;',
]
if (data.remoteStack) {
console.groupCollapsed(...logArgs)
console.log(data.remoteStack)
console.groupEnd()
} else console.log(...logArgs)
}
const result = await promise
if (result === AsyncCallIgnoreResponse) return
return SuccessResponse(data.id, await promise, !!noUndefinedKeeping)
} else {
return ErrorResponse.InvalidRequest(data.id)
}
const result = await promise
if (result === AsyncCallIgnoreResponse) return
return SuccessResponse(data.id, await promise)
} catch (e) {

@@ -306,6 +359,6 @@ if (typeof e === 'object' && 'stack' in e)

if (logLocalError) console.error(e)
return ErrorResponse(data.id, -1, e?.message, e?.stack, e)
return ErrorResponseMapped(data, e, mapError || defaultErrorMapper(sendLocalStack ? e.stack : void 0))
}
}
async function onResponse(data: Response): Promise<void> {
async function onResponse(data: Response): Promise<undefined> {
let errorMessage = '',

@@ -316,6 +369,14 @@ remoteErrorStack = '',

if (hasKey(data, 'error')) {
errorMessage = data.error.message
errorCode = data.error.code
remoteErrorStack = data.error.data?.stack ?? '<remote stack not available>'
errorType = data.error.data?.type || 'Error'
const e = data.error
errorMessage = e.message
errorCode = e.code
const detail = e.data
if (isObject(detail) && hasKey(detail, 'stack') && typeof detail.stack === 'string')
remoteErrorStack = detail.stack
else remoteErrorStack = '<remote stack not available>'
if (isObject(detail) && hasKey(detail, 'type') && typeof detail.type === 'string') errorType = detail.type
else errorType = 'Error'
if (logRemoteError)

@@ -350,2 +411,3 @@ logType === 'basic'

}
return undefined
}

@@ -367,3 +429,3 @@ message.on(key, async (_: unknown) => {

if (banUnknownMessage) {
await send(ErrorResponse.InvalidRequest((data as any).id || null))
await send(ErrorResponse.InvalidRequest((data as any).id ?? null))
} else {

@@ -374,8 +436,8 @@ // ? Ignore this message. The message channel maybe also used to transfer other message too.

} catch (e) {
console.error(e, data, result)
send(ErrorResponse.ParseError(e?.stack))
if (logLocalError) console.error(e, data, result)
send(ErrorResponse.ParseError(e, mapError || defaultErrorMapper(e?.stack)))
}
async function send(res?: Response | (Response | undefined)[]) {
if (Array.isArray(res)) {
const reply = res.map((x) => x).filter((x) => x!.id !== undefined)
const reply = res.filter((x) => hasKey(x, 'id'))
if (reply.length === 0) return

@@ -386,3 +448,3 @@ message.emit(key, await serializer.serialization(reply))

// ? This is a Notification, we MUST not return it.
if (res.id === undefined) return
if (!hasKey(res, 'id')) return
message.emit(key, await serializer.serialization(res))

@@ -395,13 +457,11 @@ }

{
get(_target, method) {
get(_target, method: string | symbol) {
let stack = removeStackHeader(new Error().stack)
return (...params: unknown[]) => {
if (typeof method !== 'string') {
if (typeof method === 'symbol') {
const internalMethod = Symbol.keyFor(method)
if (internalMethod) method = internalMethod
} else return Promise.reject(new TypeError('[AsyncCall] Only string can be the method name'))
if (typeof method === 'symbol') {
const internalMethod = Symbol.keyFor(method)
if (internalMethod) method = internalMethod
} else if (method.startsWith('rpc.'))
return Promise.reject(
new TypeError('[AsyncCall] You cannot call JSON RPC internal methods directly'),
new TypeError("[AsyncCall] Can't call JSON RPC internal methods directly"),
)

@@ -415,3 +475,3 @@ if (preferLocalImplementation && resolvedThisSideImplementation && typeof method === 'string') {

return new Promise((resolve, reject) => {
const id = generateRandomID()
const id = idGenerator()
const [param0] = params

@@ -437,15 +497,8 @@ const sendingStack = sendLocalStack ? stack : ''

async function handleSingleMessage(data: SuccessResponse | ErrorResponse | Request) {
if (hasKey(data, 'method')) {
return onRequest(data)
} else if ('error' in data || 'result' in data) {
onResponse(data)
} else {
if ('resultIsUndefined' in data) {
;(data as any).result = undefined
onResponse(data)
} else return ErrorResponse.InvalidRequest((data as any).id)
}
return undefined
async function handleSingleMessage(
data: SuccessResponse | ErrorResponse | Request,
): Promise<SuccessResponse | ErrorResponse | undefined> {
if (hasKey(data, 'method')) return onRequest(data)
return onResponse(data)
}
}
/**
* A light implementation of {@link https://www.jsonrpc.org/specification | JSON RPC 2.0}
* @packageDocumentation
* @see https://github.com/Jack-Works/async-call
* @remarks

@@ -6,0 +5,0 @@ * See the introduction at {@link https://github.com/Jack-Works/async-call | Github}

@@ -7,9 +7,9 @@ //#region Console

export interface Console {
debug(...args: unknown[]): void
debug?(...args: unknown[]): void
log(...args: unknown[]): void
groupCollapsed(...args: unknown[]): void
groupEnd(...args: unknown[]): void
error(...args: unknown[]): void
groupCollapsed?(...args: unknown[]): void
groupEnd?(...args: unknown[]): void
error?(...args: unknown[]): void
}
export function getConsole(_console?: Console): Console {
export function getConsole(_console?: Console): Required<Console> {
const console: Console = _console || (globalThis as any).console

@@ -16,0 +16,0 @@ const defaultLog = (...args: unknown[]) => console.log(...args)

@@ -20,3 +20,3 @@ class CustomError extends Error {

*/
export function RecoverError(type: string, message: string, code: number, stack: string) {
export function RecoverError(type: string, message: string, code: number, stack: string): Error {
try {

@@ -23,0 +23,0 @@ if (type.startsWith(DOMExceptionHeader) && DOMException) {

@@ -1,1 +0,1 @@

export const AsyncCallIgnoreResponse = Symbol.for('AsyncCall: This response should be ignored.')
export const AsyncCallIgnoreResponse = Symbol.for('AsyncCall/ignored')
import { DOMException, DOMExceptionHeader } from './error'
import { ErrorMapFunction } from '../Async-Call'
const jsonrpc = '2.0'
type ID = string | number | null | undefined
export const jsonrpc = '2.0'
export type ID = string | number | null | undefined
/**
* JSONRPC Request object.
*/
export interface Request
extends Readonly<{
jsonrpc: typeof jsonrpc
id?: ID
method: string
params: readonly unknown[] | object
remoteStack?: string
}> {}
type JSONRPC = Readonly<{
jsonrpc: typeof jsonrpc
id: ID
}>
export type Request = Readonly<{
method: string
params: readonly unknown[] | object
}> & { remoteStack?: string } & JSONRPC
export function Request(id: ID, method: string, params: readonly unknown[] | object, remoteStack: string): Request {
export function Request(id: ID, method: string, params: readonly unknown[] | object, remoteStack?: string): Request {
const x: Request = { jsonrpc, id, method, params, remoteStack }
if (remoteStack.length === 0) delete x.remoteStack
deleteUndefined(x, 'id')
deleteFalsy(x, 'remoteStack')
return x
}
export type SuccessResponse = Readonly<{ result: unknown }> & { resultIsUndefined?: boolean } & JSONRPC
export function SuccessResponse(id: ID, result: any, noUndefinedKeeping: boolean): SuccessResponse {
const x: SuccessResponse = { jsonrpc, id, result: result === undefined ? null : result }
if (!noUndefinedKeeping && result === undefined) {
x.resultIsUndefined = true
}
/**
* JSONRPC SuccessResponse object.
*/
export interface SuccessResponse
extends Readonly<{
jsonrpc: typeof jsonrpc
id?: ID
result: unknown
}> {}
export function SuccessResponse(id: ID, result: unknown): SuccessResponse {
const x: SuccessResponse = { jsonrpc, id, result }
deleteUndefined(x, 'id')
return x
}
export type ErrorResponse = JSONRPC &
Readonly<{
error: { code: number; message: string; data?: { stack?: string; type?: string } }
}>
/**
* JSONRPC ErrorResponse object.
* @public
*/
export interface ErrorResponse<E = unknown>
extends Readonly<{
jsonrpc: typeof jsonrpc
id?: ID
error: Readonly<{ code: number; message: string; data?: E }>
}> {}
export function ErrorResponse(id: ID, code: number, message: string, stack: string, e?: unknown): ErrorResponse {
export function ErrorResponse<T>(id: ID, code: number, message: string, data?: T): ErrorResponse<T> {
if (id === undefined) id = null
code = Math.floor(code)
if (Number.isNaN(code)) code = -1
const x: ErrorResponse<T> = { jsonrpc, id, error: { code, message, data } }
deleteUndefined(x.error, 'data')
return x
}
// Pre defined error in section 5.1
ErrorResponse.ParseError = <T>(e: unknown, mapper: ErrorMapFunction<T>): ErrorResponse<T> => {
const obj = ErrorResponseMapped({} as any, e, mapper)
const o = obj.error as Mutable<ErrorResponse['error']>
o.code = -32700
o.message = 'Parse error'
return obj
}
// Not using.
// InvalidParams -32602 'Invalid params'
// InternalError -32603 'Internal error'
ErrorResponse.InvalidRequest = (id: ID) => ErrorResponse(id, -32600, 'Invalid Request')
ErrorResponse.MethodNotFound = (id: ID) => ErrorResponse(id, -32601, 'Method not found')
type AsyncCallErrorDetail = {
stack?: string
type?: string
}
export function ErrorResponseMapped<T>(request: Request, e: unknown, mapper: ErrorMapFunction<T>): ErrorResponse<T> {
const { id } = request
const { code, message, data } = mapper(e, request)
return ErrorResponse(id, code, message, data)
}
export const defaultErrorMapper = (stack = '', code = -1): ErrorMapFunction<AsyncCallErrorDetail> => (e) => {
let message = ''
if (isObject(e) && hasKey(e, 'message') && typeof e.message === 'string') message = e.message
let type = toString('Error', () => (e as any)?.constructor?.name)

@@ -41,17 +93,9 @@ if (DOMException && e instanceof DOMException) type = DOMExceptionHeader + e.name

}
if (id === undefined) id = null
code = Math.floor(code)
if (Number.isNaN(code)) code = -1
const error: ErrorResponse['error'] = { code, message, data: { stack, type } }
return { error, id, jsonrpc }
const data: AsyncCallErrorDetail = stack ? { stack, type } : { type }
return { code, message, data }
}
// Pre defined error in section 5.1
ErrorResponse.ParseError = (stack = '') => ErrorResponse(null, -32700, 'Parse error', stack)
ErrorResponse.InvalidRequest = (id: ID) => ErrorResponse(id, -32600, 'Invalid Request', '')
ErrorResponse.MethodNotFound = (id: ID) => ErrorResponse(id, -32601, 'Method not found', '')
ErrorResponse.InvalidParams = (id: ID) => ErrorResponse(id, -32602, 'Invalid params', '')
ErrorResponse.InternalError = (id: ID, message: string = '') =>
ErrorResponse(id, -32603, 'Internal error' + message, '')
/**
* A JSONRPC response object
*/
export type Response = SuccessResponse | ErrorResponse

@@ -92,1 +136,8 @@

}
function deleteUndefined<O>(x: O, key: keyof O) {
if (x[key] === undefined) delete x[key]
}
function deleteFalsy<T>(x: T, key: keyof T) {
if (!x[key]) delete x[key]
}
type Mutable<T> = { -readonly [key in keyof T]: T[key] }
import { AsyncCallOptions, AsyncCallLogLevel, AsyncCallStrictJSONRPC } from '../Async-Call'
export function normalizeLogOptions(log: AsyncCallOptions['log']): AsyncCallLogLevel {
export function normalizeLogOptions(log: NonNullable<AsyncCallOptions['log']>): AsyncCallLogLevel {
if (typeof log !== 'boolean') return log

@@ -11,3 +11,3 @@ return {

}
export function normalizeStrictOptions(strict: AsyncCallOptions['strict']): AsyncCallStrictJSONRPC {
export function normalizeStrictOptions(strict: NonNullable<AsyncCallOptions['strict']>): AsyncCallStrictJSONRPC {
if (typeof strict !== 'boolean') return strict

@@ -17,4 +17,3 @@ return {

unknownMessage: strict,
noUndefined: strict,
}
}
import { removeStackHeader } from './error'
declare const document: any
export async function preservePauseOnException(stackCallback: (x: string) => void, f: Function, args: any[]) {
const iframe = document.createElement('iframe')
export async function preservePauseOnException(
stackCallback: (x: string) => void,
f: Function,
thisBinding: any,
args: any[],
) {
const promise = new Promise((resolve, reject) => {
let iframe: any = {}
async function executor() {
stackCallback(removeStackHeader(new Error().stack))
// receive the return value
resolve(await f(...args))
resolve(await f.apply(thisBinding, args))
}
try {
iframe = document.createElement('iframe')
iframe.style.display = 'none'

@@ -25,12 +30,16 @@ document.body.appendChild(iframe)

})
window.addEventListener('unhandledrejection', (e: any) => reject(e.reason))
window.onunhandledrejection = (e: any) => reject(e.reason)
button.click()
}
} catch (e) {
console.error('Please close preservePauseOnException.', e)
try {
// @ts-expect-error
console.error('Please close preservePauseOnException.', e)
} catch {}
return resolve(f(...args))
} finally {
iframe?.remove?.()
}
})
promise.finally(() => iframe.remove())
return promise
}
//#region Serialization
import { isObject, hasKey } from './jsonrpc'
/**

@@ -25,6 +28,6 @@ * Serialization and deserialization of the JSON RPC payload

export const NoSerialization: Serialization = {
async serialization(from) {
serialization(from) {
return from
},
async deserialization(serialized) {
deserialization(serialized) {
return serialized

@@ -39,2 +42,10 @@ },

* @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}

@@ -44,13 +55,33 @@ * @public

export const JSONSerialization = (
replacerAndReceiver: [Parameters<JSON['stringify']>[1], Parameters<JSON['parse']>[1]] = [undefined, undefined],
replacerAndReceiver: [((key: string, value: any) => any)?, ((key: string, value: any) => any)?] = [
undefined,
undefined,
],
space?: string | number | undefined,
) =>
({
async serialization(from) {
return JSON.stringify(from, replacerAndReceiver[0], space)
},
async deserialization(serialized) {
return JSON.parse(serialized as string, replacerAndReceiver[1])
},
} as Serialization)
undefinedKeepingBehavior: 'keep' | 'null' | false = 'null',
): Serialization => ({
serialization(from) {
if (undefinedKeepingBehavior && isObject(from) && hasKey(from, 'result') && from.result === undefined) {
const alt = Object.assign({}, from)
alt.result = null
if (undefinedKeepingBehavior === 'keep') (alt as any).undef = true
from = alt
}
return JSON.stringify(from, replacerAndReceiver[0], space)
},
deserialization(serialized) {
const result = JSON.parse(serialized as string, replacerAndReceiver[1])
if (
isObject(result) &&
hasKey(result, 'result') &&
result.result === null &&
hasKey(result, 'undef') &&
result.undef === true
) {
result.result = undefined
delete result.undef
}
return result
},
})
//#endregion
{
"compilerOptions": {
"plugins": [
{
"transform": "@magic-works/ttypescript-browser-like-import-transformer",
"after": true,
"extName": ".ts"
// Configs go here.
}
],
"target": "es2018" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,

@@ -28,5 +20,9 @@ "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,

"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
"rootDir": "./src/"
"rootDir": "./src/",
// This lib does not require any env
"typeRoots": [],
"types": [],
"esModuleInterop": true
},
"include": ["./src/**/*.ts"]
}

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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc