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 1.0.0 to 1.0.2

README.md

9

CHANGELOG.md

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

### [1.0.2](https://github.com/Jack-Works/async-call/compare/v1.0.1...v1.0.2) (2019-12-23)
### [1.0.1](https://github.com/Jack-Works/async-call/compare/v1.0.0...v1.0.1) (2019-12-23)
### Bug Fixes
* change the package name ([234440c](https://github.com/Jack-Works/async-call/commit/234440c2a63d01aeea4f213ee5c07a7ecf9cb29b))
## 1.0.0 (2019-09-21)

@@ -7,0 +16,0 @@

26

package.json
{
"name": "async-call-rpc",
"version": "1.0.0",
"version": "1.0.2",
"type": "module",
"description": "A lightweight JSON RPC server & client",
"main": "out/index.js",
"module": "out/index.js",
"scripts": {
"release": "standard-version",
"start": "tsc --watch",
"prepublishOnly": "rimraf ./out && tsc"
"start": "node ./watch.js",
"prepublishOnly": "rimraf ./out && tsc",
"doc:api": "api-extractor run --local --verbose",
"doc:md": "api-documenter markdown -o docs -i temp",
"doc:preview": "docsify serve ."
},

@@ -15,3 +20,5 @@ "repository": {

},
"keywords": ["jsonrpc"],
"keywords": [
"jsonrpc"
],
"author": "Jack Works <zjwpeter@gmail.com>",

@@ -24,7 +31,12 @@ "license": "MIT",

"devDependencies": {
"@microsoft/api-extractor": "latest",
"@microsoft/api-documenter": "latest",
"rimraf": "^3.0.0",
"standard-version": "^7.0.0",
"typescript": "^3.6.3"
"standard-version": "^7.0.1",
"typescript": "^3.7.2"
},
"files": ["src", "out"]
"files": [
"src",
"out"
]
}

@@ -0,28 +1,33 @@

/**
* See the document at https://github.com/Jack-Works/async-call/
*/
import {
AsyncCallOptions,
AsyncCall,
calcStrictOptions,
generateRandomID,
$AsyncIteratorStart,
$AsyncIteratorNext,
$AsyncIteratorReturn,
$AsyncIteratorThrow,
$AsyncCallIgnoreResponse,
_calcStrictOptions,
_generateRandomID,
_AsyncIteratorStart,
_AsyncIteratorNext,
_AsyncIteratorReturn,
_AsyncIteratorThrow,
_AsyncCallIgnoreResponse,
} from './Async-Call'
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>>
}
/**
* Unbox the Promise<T> into T if possible
* @internal
*/
export type UnboxPromise<T> = T extends PromiseLike<infer U> ? U : T
export type _UnboxPromise<T> = T extends PromiseLike<infer U> ? U : T
/**
* Make all generator in the type T becomes AsyncGenerator
* @internal
*/
export type MakeAllGeneratorFunctionsAsync<T> = {
export type _AsyncGeneratorVersionOf<T> = {
[key in keyof T]: T[key] extends (

@@ -33,4 +38,4 @@ ...args: infer Args

...args: Args
) => AsyncIterator<UnboxPromise<Yield>, UnboxPromise<Return>, UnboxPromise<Next>> & {
[Symbol.asyncIterator](): AsyncIterator<UnboxPromise<Yield>, UnboxPromise<Return>, UnboxPromise<Next>>
) => AsyncIterator<_UnboxPromise<Yield>, _UnboxPromise<Return>, _UnboxPromise<Next>> & {
[Symbol.asyncIterator](): AsyncIterator<_UnboxPromise<Yield>, _UnboxPromise<Return>, _UnboxPromise<Next>>
}

@@ -40,10 +45,45 @@ : T[key]

/**
* This function provides the async generator version of the AsyncCall
* The async generator version of the AsyncCall
* @param thisSideImplementation - The implementation when this AsyncCall acts as a JSON RPC server.
* @param options - {@link AsyncCallOptions}
* @typeParam OtherSideImplementedFunctions - The type of the API that server expose. For any function on this interface, AsyncCall will convert it to the Promised type.
* @remarks
*
* To use AsyncGeneratorCall, the server and the client MUST support the following JSON RPC internal methods:
*
* Warning: Due to technical limitation, AsyncGeneratorCall will leak memory before
* {@link https://github.com/tc39/proposal-weakrefs | the ECMAScript WeakRef proposal} shipped.
*
* - `rpc.async-iterator.start`
*
* - `rpc.async-iterator.next`
*
* - `rpc.async-iterator.return`
*
* - `rpc.async-iterator.throw`
*
* @example
* ```ts
* const server = {
* async *generator() {
* let last = 0
* while (true) yield last++
* },
* }
* type Server = typeof server
* const serverRPC = AsyncGeneratorCall<Server>({}, { messageChannel })
* async function main() {
* for await (const x of serverRPC.generator()) {
* console.log('Server yielded number', x)
* }
* }
* ```
* @public
*/
export function AsyncGeneratorCall<OtherSideImplementedFunctions = {}>(
implementation: object = {},
thisSideImplementation: object = {},
options: Partial<AsyncCallOptions> & Pick<AsyncCallOptions, 'messageChannel'>,
): MakeAllGeneratorFunctionsAsync<OtherSideImplementedFunctions> {
): _AsyncGeneratorVersionOf<OtherSideImplementedFunctions> {
const iterators = new Map<string, Iterator<unknown> | AsyncIterator<unknown>>()
const strict = calcStrictOptions(options.strict || false)
const strict = _calcStrictOptions(options.strict || false)
function findIterator(id: string, label: string) {

@@ -53,3 +93,3 @@ const it = iterators.get(id)

if (strict.methodNotFound) throw new Error(`Remote iterator not found while executing ${label}`)
else return $AsyncCallIgnoreResponse
else return _AsyncCallIgnoreResponse
}

@@ -59,27 +99,27 @@ return it

const server = {
[$AsyncIteratorStart](method, args) {
const iteratorGenerator: unknown = Reflect.get(implementation, method)
[_AsyncIteratorStart](method, args) {
const iteratorGenerator: unknown = Reflect.get(thisSideImplementation, method)
if (typeof iteratorGenerator !== 'function') {
if (strict.methodNotFound) throw new Error(method + ' is not a function')
else return $AsyncCallIgnoreResponse
else return _AsyncCallIgnoreResponse
}
const iterator = iteratorGenerator(...args)
const id = generateRandomID()
const id = _generateRandomID()
iterators.set(id, iterator)
return Promise.resolve(id)
},
[$AsyncIteratorNext](id, val) {
[_AsyncIteratorNext](id, val) {
const it = findIterator(id, 'next')
if (it !== $AsyncCallIgnoreResponse) return it.next(val as any)
if (it !== _AsyncCallIgnoreResponse) return it.next(val as any)
return it
},
[$AsyncIteratorReturn](id, val) {
[_AsyncIteratorReturn](id, val) {
const it = findIterator(id, 'return')
if (it !== $AsyncCallIgnoreResponse) return it.return!(val)
return $AsyncCallIgnoreResponse
if (it !== _AsyncCallIgnoreResponse) return it.return!(val)
return _AsyncCallIgnoreResponse
},
[$AsyncIteratorThrow](id, val) {
[_AsyncIteratorThrow](id, val) {
const it = findIterator(id, 'throw')
if (it !== $AsyncCallIgnoreResponse) return it.throw!(val)
return $AsyncCallIgnoreResponse
if (it !== _AsyncCallIgnoreResponse) return it.throw!(val)
return _AsyncCallIgnoreResponse
},

@@ -94,12 +134,12 @@ } as AsyncGeneratorInternalMethods

return function(...args: unknown[]) {
const id = remote[$AsyncIteratorStart](key, args)
const id = remote[_AsyncIteratorStart](key, args)
return new (class implements AsyncIterableIterator<unknown>, AsyncIterator<unknown, unknown, unknown> {
async return(val: unknown) {
return remote[$AsyncIteratorReturn](await id, val)
return remote[_AsyncIteratorReturn](await id, val)
}
async next(val?: unknown) {
return remote[$AsyncIteratorNext](await id, val)
return remote[_AsyncIteratorNext](await id, val)
}
async throw(val?: unknown) {
return remote[$AsyncIteratorThrow](await id, val)
return remote[_AsyncIteratorThrow](await id, val)
}

@@ -113,3 +153,3 @@ [Symbol.asyncIterator]() {

}
return new Proxy({}, { get: proxyTrap }) as MakeAllGeneratorFunctionsAsync<OtherSideImplementedFunctions>
return new Proxy({}, { get: proxyTrap }) as _AsyncGeneratorVersionOf<OtherSideImplementedFunctions>
}
/**
* This is a light implementation of JSON RPC 2.0
*
* https://www.jsonrpc.org/specification
*
* -----------------------------------------------------------------------------
* Extends to the specification:
*
* Request object:
* remoteStack?: string
* This property will help server print the log better.
*
* Error object:
* data?: { stack?: string, type?: string }
* This property will help client to build a better Error object.
* Supported value for "type" field (Defined in ECMAScript standard):
* Error, EvalError, RangeError, ReferenceError,
* SyntaxError, TypeError, URIError
*
* Response object:
* resultIsUndefined?: boolean
* This property is a hint. If the client is run in JavaScript,
* it should treat "result: null" as "result: undefined"
* -----------------------------------------------------------------------------
* Implemented JSON RPC extension (internal methods):
* None
* See the document at https://github.com/Jack-Works/async-call/
*/
//#region Serialization
/**
* Define how to do serialization and deserialization of remote procedure call
* Serialization and deserialization of the JSON RPC payload
* @public
*/

@@ -45,4 +21,7 @@ export interface Serialization {

}
/**
* Serialization implementation that do nothing
* @remarks {@link Serialization}
* @public
*/

@@ -57,9 +36,13 @@ export const NoSerialization: Serialization = {

}
/**
* Serialization implementation by JSON.parse/stringify
* Create a serialization by JSON.parse/stringify
*
* @param replacerAndReceiver - Replacer of 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.
* @remarks {@link Serialization}
* @public
*/
export const JSONSerialization = (
[replacer, receiver]: [Parameters<JSON['stringify']>[1], Parameters<JSON['parse']>[1]] = [undefined, undefined],
replacerAndReceiver: [Parameters<JSON['stringify']>[1], Parameters<JSON['parse']>[1]] = [undefined, undefined],
space?: string | number | undefined,

@@ -69,29 +52,114 @@ ) =>

async serialization(from) {
return JSON.stringify(from, replacer, space)
return JSON.stringify(from, replacerAndReceiver[0], space)
},
async deserialization(serialized) {
return JSON.parse(serialized as string, receiver)
return JSON.parse(serialized as string, replacerAndReceiver[1])
},
} as Serialization)
/**
* What should AsyncCall log to console.
* @public
*/
export interface AsyncCallLogLevel {
/**
* Print the log from the client when act as server
* @defaultValue true
*/
beCalled?: boolean
/**
* Print errors of self when act as a server
* @defaultValue true
*/
localError?: boolean
/**
* Print remote errors when act as a client
* @defaultValue true
*/
remoteError?: boolean
/**
* Send the local call stack to remote when act as a client
* @defaultValue false
*/
sendLocalStack?: boolean
/**
* How to print the log, 'pretty' is recommended in browser.
* @defaultValue 'pretty'
*/
type?: 'basic' | 'pretty'
}
/**
* Control the behavior that different from the JSON RPC spec.
* @public
*/
export interface AsyncCallStrictJSONRPC {
/**
* Return an error when the requested method is not defined
* @defaultValue false
*/
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
*/
unknownMessage?: boolean
}
//#endregion
/**
* Options for {@link AsyncCall}
* @public
*/
export interface AsyncCallOptions {
/**
* A key to prevent collision with other AsyncCalls. Can be anything, but need to be the same on the both side.
* A key to prevent collision with other AsyncCalls.
*
* @remarks
* The value can be anything, but need to be same on both sides.
*
* This option is useful when you want to run multiple AsyncCall instances on the same message channel.
*
* @example
* these two AsyncCall run on the same channel but they won't affect each other.
* ```ts
* AsyncCall({}, { messageChannel, key: 'app1' })
* AsyncCall({}, { messageChannel, key: 'app2' })
* ```
*
* @defaultValue `default-jsonrpc`
*/
key: string
/**
* How to serialization and deserialization parameters and return values
* How to serialization and deserialization JSON RPC payload
*
* @remarks
* We offer some built-in serializer:
* - NoSerialization (Do not do any serialization)
* - JSONSerialization (Use JSON.parse/stringify)
* See {@link Serialization}.
* There is some built-in serializer:
*
* - {@link NoSerialization} (Do not do any serialization)
*
* - {@link JSONSerialization} (Use JSON.parse/stringify)
*
* @defaultValue {@link NoSerialization}
*/
serializer: Serialization
/**
* A class that can let you transfer messages between two sides
* The message channel can let you transport messages between server and client
* @example
* ```ts
* const messageChannel = {
* on(event, callback) {
* document.addEventListener('remote-data', x => callback(x.details))
* }
* emit(event, data) {
* fetch('/server', { body: data })
* }
* }
* ```
*/

@@ -102,26 +170,16 @@ messageChannel: {

}
/** Log what to console */
log:
| {
beCalled?: boolean
localError?: boolean
remoteError?: boolean
sendLocalStack?: boolean
type?: 'basic' | 'pretty'
}
| boolean
/** Strict options */
strict:
| {
/** if method not found, return an error */
methodNotFound?: boolean
/** do not try to keep `undefined` during transfer (if true, undefined will become null) */
noUndefined?: boolean
/** send an error when receive unknown message on the channel */
unknownMessage?: boolean
}
| boolean
/**
* Choose log level. See {@link AsyncCallLogLevel}
* @defaultValue true
*/
log: AsyncCallLogLevel | boolean
/**
* Strict options. See {@link AsyncCallStrictJSONRPC}
* @defaultValue false
*/
strict: AsyncCallStrictJSONRPC | boolean
/**
* How parameters passed to remote
* https://www.jsonrpc.org/specification#parameter_structures
* @remarks
* See {@link https://www.jsonrpc.org/specification#parameter_structures}
* @defaultValue "by-position"

@@ -131,3 +189,5 @@ */

/**
* If `implementation` has the function required, call it directly instead of send it to remote.
* 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.
* @defaultValue false

@@ -137,6 +197,8 @@ */

}
/**
* Make all function in the type T Async
* @internal
*/
export type MakeAllFunctionsAsync<T> = {
export type _AsyncVersionOf<T> = {
[key in keyof T]: T[key] extends (...args: infer Args) => infer Return

@@ -146,3 +208,3 @@ ? Return extends PromiseLike<infer U>

: (...args: Args) => Promise<Return>
: T[key]
: never
}

@@ -158,67 +220,19 @@

} as const)
/**
* Async call between different context.
* Create a RPC server & client.
*
* @remarks
* Async call is a high level abstraction of MessageCenter.
* See {@link AsyncCallOptions}
*
* # Shared code
*
* - How to stringify/parse parameters/returns should be shared, defaults to NoSerialization.
*
* - `key` should be shared.
*
* # One side
*
* - Should provide some functions then export its type (for example, `BackgroundCalls`)
*
* - `const call = AsyncCall<ForegroundCalls>(backgroundCalls)`
*
* - Then you can `call` any method on `ForegroundCalls`
*
* # Other side
*
* - Should provide some functions then export its type (for example, `ForegroundCalls`)
*
* - `const call = AsyncCall<BackgroundCalls>(foregroundCalls)`
*
* - Then you can `call` any method on `BackgroundCalls`
*
* Note: Two sides can implement the same function
*
* @example
* For example, here is a mono repo.
*
* Code for UI part:
* ```ts
* const UI = {
* async dialog(text: string) {
* alert(text)
* },
* }
* export type UI = typeof UI
* const callsClient = AsyncCall<Server>(UI)
* callsClient.sendMail('hello world', 'what')
* ```
*
* Code for server part
* ```ts
* const Server = {
* async sendMail(text: string, to: string) {
* return true
* }
* }
* export type Server = typeof Server
* const calls = AsyncCall<UI>(Server)
* calls.dialog('hello')
* ```
*
* @param implementation - Implementation of this side.
* @param options - Define your own serializer, MessageCenter or other options.
*
* @param thisSideImplementation - The implementation when this AsyncCall acts as a JSON RPC server.
* @param options - {@link AsyncCallOptions}
* @typeParam OtherSideImplementedFunctions - The type of the API that server expose. For any function on this interface, AsyncCall will convert it to the Promised type.
* @returns Same as the `OtherSideImplementedFunctions` type parameter, but every function in that interface becomes async and non-function value is removed.
* @public
*/
export function AsyncCall<OtherSideImplementedFunctions = {}>(
implementation: object = {},
thisSideImplementation: object = {},
options: Partial<AsyncCallOptions> & Pick<AsyncCallOptions, 'messageChannel'>,
): MakeAllFunctionsAsync<OtherSideImplementedFunctions> {
): _AsyncVersionOf<OtherSideImplementedFunctions> {
const { serializer, key, strict, log, parameterStructures, preferLocalImplementation } = {

@@ -233,3 +247,3 @@ ...AsyncCallDefaultOptions,

unknownMessage: banUnknownMessage = false,
} = calcStrictOptions(strict)
} = _calcStrictOptions(strict)
const {

@@ -241,5 +255,5 @@ beCalled: logBeCalled = true,

sendLocalStack = false,
} = calcLogOptions(log)
} = _calcLogOptions(log)
const console = getConsole()
type PromiseParam = Parameters<(ConstructorParameters<typeof Promise>)[0]>
type PromiseParam = Parameters<ConstructorParameters<typeof Promise>[0]>
const requestContext = new Map<string | number, { f: PromiseParam; stack: string }>()

@@ -252,4 +266,4 @@ async function onRequest(data: Request): Promise<Response | undefined> {

? Symbol.for(data.method)
: data.method) as keyof typeof implementation
const executor: unknown = implementation[key]
: data.method) as keyof typeof thisSideImplementation
const executor: unknown = thisSideImplementation[key]
if (!executor || typeof executor !== 'function') {

@@ -293,3 +307,3 @@ if (!banMethodNotFound) {

const result = await promise
if (result === $AsyncCallIgnoreResponse) return
if (result === _AsyncCallIgnoreResponse) return
return new SuccessResponse(data.id, await promise, !!noUndefinedKeeping)

@@ -404,3 +418,3 @@ } else {

if (preferLocalImplementation && typeof method === 'string') {
const localImpl: unknown = implementation[method as keyof typeof implementation]
const localImpl: unknown = thisSideImplementation[method as keyof typeof thisSideImplementation]
if (localImpl && typeof localImpl === 'function') {

@@ -417,3 +431,3 @@ return new Promise((resolve, reject) => {

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

@@ -437,3 +451,3 @@ const sendingStack = sendLocalStack ? stack : ''

},
) as MakeAllFunctionsAsync<OtherSideImplementedFunctions>
) as _AsyncVersionOf<OtherSideImplementedFunctions>

@@ -455,12 +469,15 @@ async function handleSingleMessage(data: SuccessResponse | ErrorResponse | Request) {

export const $AsyncIteratorStart = Symbol.for('rpc.async-iterator.start')
export const $AsyncIteratorNext = Symbol.for('rpc.async-iterator.next')
export const $AsyncIteratorReturn = Symbol.for('rpc.async-iterator.return')
export const $AsyncIteratorThrow = Symbol.for('rpc.async-iterator.throw')
export const $AsyncCallIgnoreResponse = Symbol.for('AsyncCall: This response should be ignored.')
/** @internal */
export const _AsyncIteratorStart = Symbol.for('rpc.async-iterator.start')
/** @internal */
export const _AsyncIteratorNext = Symbol.for('rpc.async-iterator.next')
/** @internal */
export const _AsyncIteratorReturn = Symbol.for('rpc.async-iterator.return')
/** @internal */
export const _AsyncIteratorThrow = Symbol.for('rpc.async-iterator.throw')
/** @internal */
export const _AsyncCallIgnoreResponse = Symbol.for('AsyncCall: This response should be ignored.')
/**
* @internal
*/
export function generateRandomID() {
/** @internal */
export function _generateRandomID() {
return Math.random()

@@ -474,3 +491,3 @@ .toString(36)

*/
export function calcLogOptions(log: AsyncCallOptions['log']): Exclude<typeof log, boolean> {
function _calcLogOptions(log: AsyncCallOptions['log']): AsyncCallLogLevel {
const logAllOn = { beCalled: true, localError: true, remoteError: true, type: 'pretty' } as const

@@ -484,3 +501,3 @@ const logAllOff = { beCalled: false, localError: false, remoteError: false, type: 'basic' } as const

*/
export function calcStrictOptions(strict: AsyncCallOptions['strict']): Exclude<typeof strict, boolean> {
export function _calcStrictOptions(strict: AsyncCallOptions['strict']): AsyncCallStrictJSONRPC {
const strictAllOn = { methodNotFound: true, unknownMessage: true, noUndefined: true }

@@ -487,0 +504,0 @@ const strictAllOff = { methodNotFound: false, unknownMessage: false, noUndefined: false }

@@ -0,2 +1,149 @@

/**
* A light implementation of {@link https://www.jsonrpc.org/specification | JSON RPC 2.0}
*
* @remarks
* Runtime requirement: At least ECMAScript 6, `globalThis`, well known Symbol `Symbol.asyncIterator` if you use Async Call remote generator function support.
*
* ====================================
*
* Install
*
* Node: `npm install async-call`
*
* Browser: `import { AsyncCall } from 'https://unpkg.com/async-call-rpc@latest/out/index.js?module'`
*
* Deno: `import { AsyncCall } from 'https://unpkg.com/async-call-rpc@latest/src/Async-Call.ts'`
*
* Other environment: You may copy the `out/Async-Call.js` or `src/Async-Call.ts` to your project.
*
* Async Call has no dependency so you can run it on any modern ECMAScript platform.
*
* ====================================
*
* Extends to the specification
*
* ```ts
* interface JSONRPC_Request_object {
* // This property will help server print the log better.
* remoteStack?: string
* }
* ```
*
* ```ts
* interface JSONRPC_Error_object {
* // This property will help client to build a better Error object.
* data?: {
* stack?: string,
* // Supported value for "type" field (Defined in ECMAScript standard):
* type?: string | 'Error' | 'EvalError' | 'RangeError'
* | 'ReferenceError' | 'SyntaxError' | 'TypeError' | 'URIError'
* }
* }
* ```
*
* ```ts
* interface JSONRPC_Response_object {
* // This property is a hint.
* // If the client is run in JavaScript,
* // it should treat "result: null" as "result: undefined"
* resultIsUndefined?: boolean
* }
* ```
*
* ====================================
*
* Implemented internal JSON RPC methods
*
* ```ts
* interface JSONRPC_Internal_Methods {
* // These 4 methods represent the Async Iterator protocol in ECMAScript
* // this method starts an async iterator, return the id
* 'rpc.async-iterator.start'(method: string, params: unknown[]): Promise<string>
* // this method executes `next` method on the previous iterator started by `rpc.async-iterator.start`
* 'rpc.async-iterator.next'(id: string, value: unknown): Promise<IteratorResult<unknown>>
* // this method executes `return` method on the previous iterator started by `rpc.async-iterator.start`
* 'rpc.async-iterator.return'(id: string, value: unknown): Promise<IteratorResult<unknown>>
* // this method executes `throw` method on the previous iterator started by `rpc.async-iterator.start`
* 'rpc.async-iterator.throw'(id: string, value: unknown): Promise<IteratorResult<unknown>>
* }
* ```
*
* @example
* - Simple usage:
*
* As a Server
* ```ts
* AsyncCall({
* async add(a: number, b: number) {
* return a + b
* }
* }, { messageChannel })
* ```
*
* As a client
* ```ts
* const server = AsyncCall<{
* add(a: number, b: number): Promise<number>
* }>({}, { messageChannel })
* server.add(1, 5).then(console.log)
* ```
*
* - Share type in a mono repo
*
* Code for UI:
* ```ts
* const UI = {
* async dialog(text: string) {
* alert(text)
* },
* }
* export type UI = typeof UI
* const callsClient = AsyncCall<Server>(UI, { messageChannel })
* callsClient.sendMail('hello world', 'what')
* ```
*
* Code for server
*
* ```ts
* const Server = {
* async sendMail(text: string, to: string) {
* return true
* }
* }
* export type Server = typeof Server
* const calls = AsyncCall<UI>(Server, { messageChannel })
* calls.dialog('hello')
* ```
*
* - A demo implementation of `messageChannel`
*
* ```ts
class PlayGroundChannel {
static server = document.createElement('a')
static client = document.createElement('a')
// actor: 'server' | 'client'
constructor(actor) {
PlayGroundChannel[actor].addEventListener('targetEventChannel', e => {
const detail = e.detail
for (const f of this.listener) {
try {
f(detail)
} catch {}
} //
})
} //
listener = []
on(_, cb) { this.listener.push(cb) }
emit(_, data) {
PlayGroundChannel[this.actor === 'client' ? 'server' : 'client'].dispatchEvent(
new CustomEvent('targetEventChannel', { detail: data }),
) //
} //
} //
```
*
* @packageDocumentation
*/
export * from './Async-Call'
export * from './Async-Call-Generator'
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