diff --git a/package-lock.json b/package-lock.json index b865d9c6..6e43ed3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,7 +96,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1677,7 +1676,6 @@ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -3165,7 +3163,6 @@ "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", @@ -4411,7 +4408,6 @@ "integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -4492,7 +4488,6 @@ "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.0", "@typescript-eslint/types": "8.56.0", @@ -5151,7 +5146,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5696,7 +5690,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6866,7 +6859,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -6927,7 +6919,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -7054,7 +7045,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -9829,7 +9819,6 @@ "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", @@ -10860,7 +10849,6 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -11389,7 +11377,6 @@ "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -12446,7 +12433,6 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -12580,7 +12566,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12707,7 +12692,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -12831,7 +12815,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -12907,7 +12890,6 @@ "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.18", "@vitest/mocker": "4.0.18", diff --git a/src/jrpc/basePostMessageStream.ts b/src/jrpc/basePostMessageStream.ts index bf440a96..aabc3fb1 100644 --- a/src/jrpc/basePostMessageStream.ts +++ b/src/jrpc/basePostMessageStream.ts @@ -23,6 +23,9 @@ export interface PostMessageEvent { source: typeof window; } +/** + * @deprecated Part of the JRPC V1 stream API. Use {@link createEngineStreamV2} instead. + */ export function isValidStreamMessage(message: unknown): message is StreamMessage { return ( typeof message === "object" && @@ -34,6 +37,9 @@ export function isValidStreamMessage(message: unknown): message is StreamMessage ); } +/** + * @deprecated Part of the JRPC V1 stream API. Use {@link createEngineStreamV2} instead. + */ export abstract class BasePostMessageStream extends Duplex { private _init: boolean; diff --git a/src/jrpc/index.ts b/src/jrpc/index.ts index 7c68ff1d..69912065 100644 --- a/src/jrpc/index.ts +++ b/src/jrpc/index.ts @@ -1,3 +1,11 @@ +// ────────────────────────────────────────────────────────────────────────────── +// JRPC V1 exports — DEPRECATED +// +// The V1 engine, middleware types, and stream utilities below are deprecated. +// Migrate to the JRPC V2 API exported from "./v2" (e.g. JRPCEngineV2, +// JRPCMiddlewareV2, createEngineStreamV2, providerFromEngineV2, etc.). +// ────────────────────────────────────────────────────────────────────────────── + export type { PostMessageEvent, StreamData, StreamMessage } from "./basePostMessageStream"; export { BasePostMessageStream, isValidStreamMessage } from "./basePostMessageStream"; export * from "./errors"; @@ -13,6 +21,6 @@ export type { SubstreamOptions } from "./substream"; export { Substream } from "./substream"; /** - * Export the v2 module. + * JRPC V2 API — preferred. */ export * from "./v2"; diff --git a/src/jrpc/interfaces.ts b/src/jrpc/interfaces.ts index ec16e08d..7681613f 100644 --- a/src/jrpc/interfaces.ts +++ b/src/jrpc/interfaces.ts @@ -31,10 +31,22 @@ export interface JRPCRequest extends JRP params?: Params; } +/** + * @deprecated Part of the JRPC V1 middleware signature. Use {@link JRPCMiddlewareV2} instead. + */ export type JRPCEngineNextCallback = (cb?: (done: (error?: Error) => void) => void) => void; +/** + * @deprecated Part of the JRPC V1 middleware signature. Use {@link JRPCMiddlewareV2} instead. + */ export type JRPCEngineEndCallback = (error?: Error) => void; +/** + * @deprecated Part of the JRPC V1 middleware signature. Use {@link JRPCMiddlewareV2} instead. + */ export type JRPCEngineReturnHandler = (done: (error?: Error) => void) => void; +/** + * @deprecated Part of the JRPC V1 stream middleware. Use {@link createEngineStreamV2} instead. + */ interface IdMapValue { req: JRPCRequest; res: JRPCResponse; @@ -42,10 +54,17 @@ interface IdMapValue { end: JRPCEngineEndCallback; } +/** + * @deprecated Part of the JRPC V1 stream middleware. Use {@link createEngineStreamV2} instead. + */ export interface IdMap { [requestId: string]: IdMapValue; } +/** + * @deprecated Use {@link JRPCMiddlewareV2} instead. V2 middleware receives a single + * params object `{ request, context, next }` and returns the result directly. + */ export type JRPCMiddleware = ( req: JRPCRequest, res: JRPCResponse, @@ -53,6 +72,9 @@ export type JRPCMiddleware = ( end: JRPCEngineEndCallback ) => void; +/** + * @deprecated Use {@link JRPCMiddlewareV2} instead — V2 middleware is async by default. + */ export type AsyncJRPCEngineNextCallback = () => Promise; export type Maybe = T | Partial | null | undefined; @@ -93,6 +115,9 @@ export interface JRPCError { stack?: string; } +/** + * @deprecated Part of the JRPC V1 API. V2 middleware returns results directly. + */ export interface PendingJRPCResponse extends JRPCBase { result?: T; error?: Error | JRPCError; @@ -102,22 +127,37 @@ export interface JRPCFailure extends JRPCBase { error: JRPCError; } +/** + * @deprecated Use {@link JRPCMiddlewareV2} instead — V2 middleware is async by default. + */ export type AsyncJRPCMiddleware = ( req: JRPCRequest, res: PendingJRPCResponse, next: AsyncJRPCEngineNextCallback ) => Promise; +/** + * @deprecated Part of the JRPC V1 middleware signature. Use {@link JRPCMiddlewareV2} instead. + */ export type ReturnHandlerCallback = (error: null | Error) => void; export type BlockData = string | string[]; export type Block = Record; +/** + * @deprecated Part of the JRPC V1 provider API. Use {@link providerFromEngineV2} instead. + */ export type SendAsyncCallBack = (err: Error, providerRes: JRPCResponse) => void; +/** + * @deprecated Part of the JRPC V1 provider API. Use {@link providerFromEngineV2} instead. + */ export type SendCallBack = (err: any, providerRes: U) => void; +/** + * @deprecated Part of the JRPC V1 API. + */ export type Payload = Partial>; export interface RequestArguments { @@ -125,6 +165,9 @@ export interface RequestArguments { params?: T; } +/** + * @deprecated Part of the JRPC V1 API. + */ export interface ExtendedJsonRpcRequest extends JRPCRequest { skipCache?: boolean; } diff --git a/src/jrpc/jrpc.ts b/src/jrpc/jrpc.ts index 12961866..31d8414d 100644 --- a/src/jrpc/jrpc.ts +++ b/src/jrpc/jrpc.ts @@ -17,6 +17,10 @@ import { import { SafeEventEmitter } from "./safeEventEmitter"; import { SerializableError } from "./serializableError"; +/** + * @deprecated Part of the JRPC V1 API. Use {@link JRPCEngineV2} which returns + * results directly from `handle()` without requiring callbacks. + */ export const getRpcPromiseCallback = (resolve: (value?: unknown) => void, reject: (error?: Error) => void, unwrapResult = true) => (error: Error, response: JRPCResponse): void => { @@ -29,6 +33,10 @@ export const getRpcPromiseCallback = } }; +/** + * @deprecated Part of the JRPC V1 API. The V2 engine handles errors internally + * via {@link JRPCEngineV2}. + */ export function createErrorMiddleware(log: ConsoleLike): JRPCMiddleware { return (req, res, next, end) => { try { @@ -54,10 +62,16 @@ export function createErrorMiddleware(log: ConsoleLike): JRPCMiddleware boolean; }; +/** + * @deprecated Part of the JRPC V1 API. Use {@link createEngineStreamV2} instead. + */ export function createStreamMiddleware(): { events: SafeEventEmitter; middleware: JRPCMiddleware; @@ -121,8 +135,14 @@ export function createStreamMiddleware(): { return { events, middleware, stream }; } +/** + * @deprecated Use {@link createScaffoldMiddlewareV2} and its handler type instead. + */ export type ScaffoldMiddlewareHandler = JRPCMiddleware | Json; +/** + * @deprecated Use {@link createScaffoldMiddlewareV2} instead. + */ export function createScaffoldMiddleware(handlers: { [methodName: string]: ScaffoldMiddlewareHandler; }): JRPCMiddleware { @@ -142,6 +162,9 @@ export function createScaffoldMiddleware(handlers: { }; } +/** + * @deprecated Part of the JRPC V1 API. The V2 engine does not require ID remapping. + */ export function createIdRemapMiddleware(): JRPCMiddleware { return (req, res, next, _end) => { const originalId = req.id; @@ -156,6 +179,9 @@ export function createIdRemapMiddleware(): JRPCMiddleware { }; } +/** + * @deprecated Part of the JRPC V1 API. Write a {@link JRPCMiddlewareV2} instead. + */ export function createLoggerMiddleware(logger: ConsoleLike): JRPCMiddleware { return (req, res, next, _) => { logger.debug("REQ", req, "RES", res); @@ -163,6 +189,9 @@ export function createLoggerMiddleware(logger: ConsoleLike): JRPCMiddleware(asyncMiddleware: AsyncJRPCMiddleware): JRPCMiddleware { return async (req, res, next, end) => { // nextPromise is the key to the implementation diff --git a/src/jrpc/jrpcEngine.ts b/src/jrpc/jrpcEngine.ts index bd9501d1..13c54eba 100644 --- a/src/jrpc/jrpcEngine.ts +++ b/src/jrpc/jrpcEngine.ts @@ -20,6 +20,10 @@ import { import { SafeEventEmitter } from "./safeEventEmitter"; import { SerializableError } from "./serializableError"; +/** + * @deprecated Use {@link JRPCEngineV2} instead, which does not extend EventEmitter. + * Notifications should be handled via a separate {@link SafeEventEmitter}. + */ export type JrpcEngineEvents = { notification: (...args: unknown[]) => void; }; @@ -43,6 +47,10 @@ function constructFallbackError(error: Error): JRPCError { /** * A JSON-RPC request and response processor. * Give it a stack of middleware, pass it requests, and get back responses. + * + * @deprecated Use {@link JRPCEngineV2} instead. The V2 engine uses a functional + * middleware signature, immutable requests, and a {@link MiddlewareContext} for + * inter-middleware communication. */ export class JRPCEngine extends SafeEventEmitter { private _middleware: JRPCMiddleware[]; @@ -396,6 +404,9 @@ export class JRPCEngine extends SafeEventEmitter { } } +/** + * @deprecated Use {@link JRPCEngineV2.create} with a middleware array instead. + */ export function mergeMiddleware(middlewareStack: JRPCMiddleware[]): JRPCMiddleware { const engine = new JRPCEngine(); middlewareStack.forEach((middleware) => { @@ -404,10 +415,16 @@ export function mergeMiddleware(middlewareStack: JRPCMiddleware void; }; +/** + * @deprecated Use {@link providerFromEngineV2} instead. + */ export interface SafeEventEmitterProvider extends SafeEventEmitter { sendAsync: (req: JRPCRequest) => Promise; send: (req: JRPCRequest, callback: SendCallBack>) => void; request: (args: RequestArguments) => Promise>; } +/** + * @deprecated Use {@link providerFromEngineV2} instead. + */ export function providerFromEngine(engine: JRPCEngine): SafeEventEmitterProvider { const provider: SafeEventEmitterProvider = new SafeEventEmitter() as SafeEventEmitterProvider; // handle both rpc send methods @@ -502,6 +528,9 @@ export function providerFromEngine(engine: JRPCEngine): SafeEventEmitterProvider return provider; } +/** + * @deprecated Use {@link providerFromMiddlewareV2} instead. + */ export function providerFromMiddleware(middleware: JRPCMiddleware): SafeEventEmitterProvider { const engine = new JRPCEngine(); engine.push(middleware); @@ -509,6 +538,9 @@ export function providerFromMiddleware(middleware: JRPCMiddleware { return async (req, res, _next, end) => { // send request to provider diff --git a/src/jrpc/postMessageStream.ts b/src/jrpc/postMessageStream.ts index 9200c1b8..be72fc01 100644 --- a/src/jrpc/postMessageStream.ts +++ b/src/jrpc/postMessageStream.ts @@ -15,6 +15,9 @@ export interface PostMessageStreamArgs extends DuplexOptions { targetWindow?: Window; } +/** + * @deprecated Part of the JRPC V1 stream API. Use {@link createEngineStreamV2} instead. + */ export class PostMessageStream extends BasePostMessageStream { private _name: string; diff --git a/src/jrpc/serializableError.ts b/src/jrpc/serializableError.ts index f5b0b2d5..224ce651 100644 --- a/src/jrpc/serializableError.ts +++ b/src/jrpc/serializableError.ts @@ -1,5 +1,8 @@ import stringify from "json-stable-stringify"; +/** + * @deprecated Use {@link JsonRpcError} from the errors module instead. + */ export class SerializableError extends Error { public code: number; diff --git a/src/jrpc/v2/compatibility-utils.ts b/src/jrpc/v2/compatibility-utils.ts index 0a00c809..be9a436d 100644 --- a/src/jrpc/v2/compatibility-utils.ts +++ b/src/jrpc/v2/compatibility-utils.ts @@ -108,10 +108,13 @@ export function propagateToRequest(req: Record, context: Middle * @param context - The context to propagate from. * @returns The mutable cloned request. */ -export function propagateToMutableRequest(req: Record, context: MiddlewareContext): Record { +export function propagateToMutableRequest>( + req: Record, + context: MiddlewareContext +): ReturnedMutableRequest { const clonedRequest = deepClone(req); propagateToRequest(clonedRequest, context); - return clonedRequest; + return clonedRequest as ReturnedMutableRequest; } /** diff --git a/test/v2/compatibility-utils.test.ts b/test/v2/compatibility-utils.test.ts index f9931e7c..e6f90f3e 100644 --- a/test/v2/compatibility-utils.test.ts +++ b/test/v2/compatibility-utils.test.ts @@ -418,6 +418,48 @@ describe("compatibility-utils", () => { (result.params as unknown[])[0] = "replaced"; expect((result.params as unknown[])[0]).toBe("replaced"); }); + + it("returns the result typed as the specified generic ReturnType", () => { + type CustomRequest = { + jsonrpc: string; + method: string; + params: JRPCParams; + id: number; + extraProp?: string; + }; + + const request = Object.freeze({ + jsonrpc, + method: "test_method", + params: Object.freeze([1]), + id: 42, + }); + const context = new MiddlewareContext>(); + context.set("extraProp", "typed-value"); + + const result = propagateToMutableRequest(request, context); + + expect(result.jsonrpc).toBe(jsonrpc); + expect(result.method).toBe("test_method"); + expect(result.id).toBe(42); + expect(result.extraProp).toBe("typed-value"); + }); + + it("defaults ReturnType to Record when no generic is provided", () => { + const request = { + jsonrpc, + method: "test_method", + params: [1], + id: 42, + }; + const context = new MiddlewareContext>(); + context.set("extra", "value"); + + const result = propagateToMutableRequest(request, context); + + expect(result.extra).toBe("value"); + expect(result.method).toBe("test_method"); + }); }); describe("deserializeError", () => {