diff --git a/.changeset/spicy-rivers-act.md b/.changeset/spicy-rivers-act.md new file mode 100644 index 0000000..9c3e4b1 --- /dev/null +++ b/.changeset/spicy-rivers-act.md @@ -0,0 +1,5 @@ +--- +"async-call-rpc": minor +--- + +rename parameterStructures to parameterStructure diff --git a/__tests__/options.ts b/__tests__/options.ts index f456cb2..ab0a27e 100644 --- a/__tests__/options.ts +++ b/__tests__/options.ts @@ -119,3 +119,14 @@ it( ).toThrowErrorMatchingInlineSnapshot(`[TypeError: Please remove key.]`) }), ) + +it( + 'should error if both parameterStructure and parameterStructures are provided', + withSnapshotDefault('async-call-key-name', async ({ init }) => { + expect(() => + init({ + options: { parameterStructure: 'by-name', parameterStructures: 'by-name' }, + }), + ).toThrowErrorMatchingInlineSnapshot(`[TypeError: Please remove key.]`) + }), +) diff --git a/api/base.api.md b/api/base.api.md index 387dded..cbd6cc7 100644 --- a/api/base.api.md +++ b/api/base.api.md @@ -28,6 +28,8 @@ export interface AsyncCallOptions; name?: string; + parameterStructure?: 'by-position' | 'by-name'; + // @deprecated parameterStructures?: 'by-position' | 'by-name'; preferLocalImplementation?: boolean; // @deprecated diff --git a/api/full.api.md b/api/full.api.md index 50a97ca..ae96552 100644 --- a/api/full.api.md +++ b/api/full.api.md @@ -28,6 +28,8 @@ export interface AsyncCallOptions; name?: string; + parameterStructure?: 'by-position' | 'by-name'; + // @deprecated parameterStructures?: 'by-position' | 'by-name'; preferLocalImplementation?: boolean; // @deprecated diff --git a/docs/async-call-rpc.asynccalloptions.md b/docs/async-call-rpc.asynccalloptions.md index 5e3a1c0..54363af 100644 --- a/docs/async-call-rpc.asynccalloptions.md +++ b/docs/async-call-rpc.asynccalloptions.md @@ -23,6 +23,7 @@ export interface AsyncCallOptions<unknown> | _(Optional)_ Change the [ErrorResponseDetail](./async-call-rpc.errorresponsedetail.md). | | [name?](./async-call-rpc.asynccalloptions.name.md) | | string | _(Optional)_ Name used when pretty log is enabled. | +| [parameterStructure?](./async-call-rpc.asynccalloptions.parameterstructure.md) | | 'by-position' \| 'by-name' | _(Optional)_ Choose flavor of parameter structures defined in the spec | | [parameterStructures?](./async-call-rpc.asynccalloptions.parameterstructures.md) | | 'by-position' \| 'by-name' | _(Optional)_ Choose flavor of parameter structures defined in the spec | | [preferLocalImplementation?](./async-call-rpc.asynccalloptions.preferlocalimplementation.md) | | boolean | _(Optional)_ Prefer local implementation than remote. | | [serializer?](./async-call-rpc.asynccalloptions.serializer.md) | | [Serialization](./async-call-rpc.serialization.md) | _(Optional)_ Serializer of the requests and responses. | diff --git a/docs/async-call-rpc.asynccalloptions.parameterstructure.md b/docs/async-call-rpc.asynccalloptions.parameterstructure.md new file mode 100644 index 0000000..a029443 --- /dev/null +++ b/docs/async-call-rpc.asynccalloptions.parameterstructure.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [async-call-rpc](./async-call-rpc.md) > [AsyncCallOptions](./async-call-rpc.asynccalloptions.md) > [parameterStructure](./async-call-rpc.asynccalloptions.parameterstructure.md) + +## AsyncCallOptions.parameterStructure property + +Choose flavor of parameter structures defined in the spec + +**Signature:** + +```typescript +parameterStructure?: 'by-position' | 'by-name'; +``` + +## Remarks + +When using `by-name`, only first parameter is sent to the remote and it must be an object. + diff --git a/docs/async-call-rpc.asynccalloptions.parameterstructures.md b/docs/async-call-rpc.asynccalloptions.parameterstructures.md index 0d6332d..ce4687d 100644 --- a/docs/async-call-rpc.asynccalloptions.parameterstructures.md +++ b/docs/async-call-rpc.asynccalloptions.parameterstructures.md @@ -4,6 +4,11 @@ ## AsyncCallOptions.parameterStructures property +> Warning: This API is now obsolete. +> +> renamed to "parameterStructure" +> + Choose flavor of parameter structures defined in the spec **Signature:** diff --git a/src/Async-Call.ts b/src/Async-Call.ts index 4b44e45..8d2403d 100644 --- a/src/Async-Call.ts +++ b/src/Async-Call.ts @@ -116,7 +116,8 @@ export function AsyncCall( name, strict = true, log = true, - parameterStructures = 'by-position', + parameterStructures: deprecatedParameterStructures, + parameterStructure, preferLocalImplementation = false, idGenerator = generateRandomID, mapError, @@ -128,6 +129,8 @@ export function AsyncCall( // Note: we're not shorten this error message because it will be removed in the next major version. if (serializer && encoder) throw new TypeError('Please remove serializer.') if (name && deprecatedName) throw new TypeError('Please remove key.') + if (deprecatedParameterStructures && parameterStructure) throw new TypeError('Please remove parameterStructure.') + const paramStyle = deprecatedParameterStructures || parameterStructure || 'by-position' const logKey = name || deprecatedName || 'rpc' const { @@ -190,8 +193,7 @@ export function AsyncCall( const { params, method, id: req_id, remoteStack } = data // ? We're mapping any method starts with 'rpc.' to a Symbol.for const key = (method.startsWith('rpc.') ? Symbol.for(method) : method) as keyof object - const executor: unknown = - resolvedThisSideImplementationValue && (resolvedThisSideImplementationValue as any)[key] + const executor: unknown = resolvedThisSideImplementationValue && resolvedThisSideImplementationValue[key] if (!isFunction(executor)) { if (!banMethodNotFound) { if (log_localError) console_debug('Missing method', key, data) @@ -307,8 +309,11 @@ export function AsyncCall( } } catch (e) { if (log_localError) console_error(e, data, result) - // TODO: should check before access e.stack - return ErrorResponseParseError(e, mapError || defaultErrorMapper(e && (e as any).stack)) + let stack: string | undefined + try { + stack = '' + (e as any).stack + } catch {} + return ErrorResponseParseError(e, mapError || defaultErrorMapper(stack)) } } const rawMessageSender = async (res: undefined | Response | (Response | undefined)[]) => { @@ -382,7 +387,7 @@ export function AsyncCall( } const id = idGenerator() stack = removeStackHeader(stack) - const param = parameterStructures === 'by-name' && args.length === 1 && isObject(args[0]) ? args[0] : args + const param = paramStyle === 'by-name' && args.length === 1 && isObject(args[0]) ? args[0] : args const request = makeRequest( notify ? undefined : id, method as string, diff --git a/src/types.ts b/src/types.ts index 62aba74..c4e4aeb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -114,6 +114,17 @@ export interface AsyncCallStrictOptions { * @defaultValue true */ unknownMessage?: boolean + // TODO: implement this if there is needed + /** + * Controls if redundant arguments on the client triggers a warning or error. + * @see {@link https://www.jsonrpc.org/specification#parameter_structures} + * @remarks + * If this option is set and parameterStructure is "by-name", + * and the client calls with more than 1 argument, it will trigger a warning or error. + * + * @defaultValue false + */ + // redundantArguments?: false | 'error' | 'warning' } /** * Strict options @@ -133,8 +144,6 @@ export interface AsyncCallOptions Math.random().toString(36).slice(2) - * @privateRemarks - * TODO: rename to generateID */ idGenerator?(): string | number /**