Skip to content

Commit 964e3f2

Browse files
committed
feat: added util func to propagate the forzen request to mutable one
1 parent 7c6cc6a commit 964e3f2

3 files changed

Lines changed: 100 additions & 3 deletions

File tree

src/jrpc/v2/compatibility-utils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,23 @@ export function propagateToRequest(req: Record<string, unknown>, context: Middle
9797
});
9898
}
9999

100+
/**
101+
* Copies non-JSON-RPC string properties from the context to the request.
102+
*
103+
* Clone the original request object and propagate the context to the cloned request.
104+
*
105+
* **ATTN:** Only string properties are copied.
106+
*
107+
* @param req - The request to propagate the context to.
108+
* @param context - The context to propagate from.
109+
* @returns The mutable cloned request.
110+
*/
111+
export function propagateToMutableRequest(req: Record<string, unknown>, context: MiddlewareContext): Record<string, unknown> {
112+
const clonedRequest = deepClone(req);
113+
propagateToRequest(clonedRequest, context);
114+
return clonedRequest;
115+
}
116+
100117
/**
101118
* Deserialize the error property for a thrown error, merging in the cause where possible.
102119
*

src/jrpc/v2/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { getUniqueId, isNotification, isRequest } from "../../utils/jrpc";
22
export { asLegacyMiddleware } from "./asLegacyMiddleware";
3-
export { deepClone, fromLegacyRequest, makeContext, propagateToContext, propagateToRequest } from "./compatibility-utils";
3+
export { deepClone, fromLegacyRequest, makeContext, propagateToContext, propagateToMutableRequest, propagateToRequest } from "./compatibility-utils";
44
export { createScaffoldMiddleware as createScaffoldMiddlewareV2 } from "./createScaffoldMiddleware";
55
export { JRPCEngineV2 } from "./jrpcEngineV2";
66
export { JRPCServer } from "./jrpcServer";

test/v2/compatibility-utils.test.ts

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
22

3-
import { Json, JRPCParams, JRPCRequest } from "../../src";
4-
import { stringify } from "../../src/utils/jrpc";
3+
import { JRPCParams, JRPCRequest, Json } from "../../src";
54
import { JsonRpcError } from "../../src/jrpc/errors";
65
import {
76
deepClone,
87
deserializeError,
98
fromLegacyRequest,
109
makeContext,
1110
propagateToContext,
11+
propagateToMutableRequest,
1212
propagateToRequest,
1313
} from "../../src/jrpc/v2/compatibility-utils";
1414
import { MiddlewareContext } from "../../src/jrpc/v2/MiddlewareContext";
15+
import { stringify } from "../../src/utils/jrpc";
1516

1617
const jsonrpc = "2.0" as const;
1718

@@ -340,6 +341,85 @@ describe("compatibility-utils", () => {
340341
});
341342
});
342343

344+
describe("propagateToMutableRequest", () => {
345+
it("returns a mutable object from a frozen request", () => {
346+
const request = Object.freeze({
347+
jsonrpc,
348+
method: "test_method",
349+
params: Object.freeze([1, 2, 3]),
350+
id: 1,
351+
});
352+
const context = new MiddlewareContext<Record<string, unknown>>();
353+
context.set("extraProp", "value");
354+
355+
const result = propagateToMutableRequest(request, context);
356+
357+
expect(Object.isFrozen(result)).toBe(false);
358+
result.newKey = "can assign";
359+
expect(result.newKey).toBe("can assign");
360+
});
361+
362+
it("does not mutate the original frozen request", () => {
363+
const request = Object.freeze({
364+
jsonrpc,
365+
method: "test_method",
366+
params: Object.freeze([1, 2, 3]),
367+
id: 1,
368+
});
369+
const context = new MiddlewareContext<Record<string, unknown>>();
370+
context.set("extraProp", "value");
371+
372+
propagateToMutableRequest(request, context);
373+
374+
expect(request).toStrictEqual({
375+
jsonrpc,
376+
method: "test_method",
377+
params: [1, 2, 3],
378+
id: 1,
379+
});
380+
expect("extraProp" in request).toBe(false);
381+
});
382+
383+
it("propagates context properties onto the mutable clone of a frozen request", () => {
384+
const request = Object.freeze({
385+
jsonrpc,
386+
method: "test_method",
387+
params: Object.freeze([1]),
388+
id: 42,
389+
});
390+
const context = new MiddlewareContext<Record<string, unknown>>();
391+
context.set("extraProp", "value");
392+
context.set("anotherProp", { nested: true });
393+
394+
const result = propagateToMutableRequest(request, context);
395+
396+
expect(result).toStrictEqual({
397+
jsonrpc,
398+
method: "test_method",
399+
params: [1],
400+
id: 42,
401+
extraProp: "value",
402+
anotherProp: { nested: true },
403+
});
404+
});
405+
406+
it("produces deeply mutable params from a frozen request", () => {
407+
const request = Object.freeze({
408+
jsonrpc,
409+
method: "test_method",
410+
params: Object.freeze([Object.freeze({ a: 1 }), 2]),
411+
id: 1,
412+
});
413+
const context = new MiddlewareContext();
414+
415+
const result = propagateToMutableRequest(request, context);
416+
417+
expect(Object.isFrozen(result.params)).toBe(false);
418+
(result.params as unknown[])[0] = "replaced";
419+
expect((result.params as unknown[])[0]).toBe("replaced");
420+
});
421+
});
422+
343423
describe("deserializeError", () => {
344424
// Requires some special handling due to the possible existence or
345425
// non-existence of Error.isError

0 commit comments

Comments
 (0)