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

r19

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

r19 - npm Package Compare versions

Comparing version 0.1.3 to 0.1.4

dist/lib/replaceLeaves.cjs

5

dist/client/createRPCClient.d.ts

@@ -18,7 +18,8 @@ /// <reference types="node" />

export type ResponseLike = {
formData(): Promise<FormData>;
arrayBuffer: () => Promise<ArrayBuffer>;
};
export type FetchLike = (input: string, init: {
method: "POST";
body: FormData;
body: any;
headers: Record<string, string>;
}) => Promise<ResponseLike>;

@@ -25,0 +26,0 @@ export type CreateRPCClientArgs = {

39

dist/client/createRPCClient.js

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

import { formDataToObject } from "../lib/formDataToObject.client.js";
import { encode, decode } from "@msgpack/msgpack";
import { replaceLeaves } from "../lib/replaceLeaves.js";
import { isErrorLike } from "../lib/isErrorLike.js";
import { objectToFormData } from "../lib/objectToFormData.client.js";
const createArbitrarilyNestedFunction = (handler, path = []) => {

@@ -20,15 +20,25 @@ return new Proxy(() => void 0, {

return createArbitrarilyNestedFunction(async (path, fnArgs) => {
const body = objectToFormData({
const preparedProcedureArgs = await replaceLeaves(fnArgs[0], async (value) => {
if (value instanceof Blob) {
return new Uint8Array(await value.arrayBuffer());
}
if (typeof value === "function") {
throw new Error("r19 does not support function arguments.");
}
return value;
});
const body = encode({
procedurePath: path,
procedureArgs: fnArgs[0]
});
procedureArgs: preparedProcedureArgs
}, { ignoreUndefined: true });
const res = await resolvedFetch(args.serverURL, {
method: "POST",
body
body,
headers: {
"Content-Type": "application/msgpack"
}
});
const formData = await res.formData();
const resObject = formDataToObject(formData);
if ("data" in resObject) {
return resObject.data;
} else {
const arrayBuffer = await res.arrayBuffer();
const resObject = decode(new Uint8Array(arrayBuffer));
if ("error" in resObject) {
const resError = resObject.error;

@@ -45,2 +55,9 @@ if (isErrorLike(resError)) {

}
} else {
return replaceLeaves(resObject.data, async (value) => {
if (value instanceof Uint8Array) {
return new Blob([value]);
}
return value;
});
}

@@ -47,0 +64,0 @@ });

@@ -1,2 +0,3 @@

import { createRouter, eventHandler, getHeader, readRawBody, setHeaders, sendStream, defineNodeMiddleware, createEvent } from "h3";
import { createRouter, eventHandler, readRawBody, setHeaders, send, defineNodeMiddleware, createEvent } from "h3";
import { Buffer } from "node:buffer";
import { handleRPCRequest } from "./handleRPCRequest.js";

@@ -6,6 +7,6 @@ const createRPCMiddleware = (args) => {

router.post("/", eventHandler(async (event) => {
const { stream, headers, statusCode } = await handleRPCRequest({
const eventBody = await readRawBody(event, false);
const { body, headers, statusCode } = await handleRPCRequest({
procedures: args.procedures,
contentTypeHeader: getHeader(event, "Content-Type"),
body: await readRawBody(event)
body: eventBody
});

@@ -16,3 +17,3 @@ if (statusCode) {

setHeaders(event, headers);
return sendStream(event, stream);
return send(event, Buffer.from(body));
}));

@@ -19,0 +20,0 @@ return defineNodeMiddleware(async (req, res) => {

/// <reference types="node" />
import { Readable } from "node:stream";
import { Buffer } from "node:buffer";
import { Procedures } from "./types";
type HandleRPCRequestArgs<TProcedures extends Procedures> = {
procedures: TProcedures;
} & ({
contentTypeHeader: string | null | undefined;
body: string | undefined;
} | {
formData: FormData;
});
body: ArrayBuffer | Buffer | undefined;
};
type HandleRPCRequestReturnType = {
stream: Readable;
body: Uint8Array;
headers: Record<string, string>;

@@ -15,0 +11,0 @@ statusCode?: number;

@@ -1,11 +0,5 @@

import { Readable } from "node:stream";
import { Buffer } from "node:buffer";
import busboy from "busboy";
import { parse } from "devalue";
import { encodeFormData } from "./lib/encodeFormData.js";
import { decode, encode } from "@msgpack/msgpack";
import { isErrorLike } from "./lib/isErrorLike.js";
import { objectToFormData } from "./lib/objectToFormData.server.js";
import { unflattenObject } from "./lib/unflattenObject.js";
import { formDataToObject } from "./lib/formDataToObject.client.js";
import { FORM_DATA_BOUNDARY_PREFIX } from "./constants.js";
import { replaceLeaves } from "./lib/replaceLeaves.js";
const findProcedure = (procedures, path) => {

@@ -26,83 +20,13 @@ path = [...path];

};
const readRPCClientArgs = async (args) => {
const bb = busboy({
headers: {
"content-type": args.contentTypeHeader
}
});
const clientArgs = {};
const promise = new Promise((resolve, reject) => {
bb.on("file", (name, file, _info) => {
const chunks = [];
file.on("data", (data) => {
chunks.push(data);
});
file.on("close", () => {
clientArgs[name] = Buffer.concat(chunks);
});
});
bb.on("field", (name, value, _info) => {
clientArgs[name] = parse(value);
});
bb.on("close", () => {
const unflattenedArgs = unflattenObject(clientArgs);
resolve(unflattenedArgs);
});
bb.on("error", (error) => {
reject(error);
});
});
Readable.from(args.body).pipe(bb);
return promise;
};
const isPlainObject = (value) => {
if (typeof value !== "object" || value === null) {
return false;
}
const prototype = Object.getPrototypeOf(value);
return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
};
const prepareRes = (res) => {
if (Array.isArray(res)) {
const preparedRes = [];
for (let i = 0; i < res.length; i++) {
preparedRes[i] = prepareRes(res[i]);
}
return preparedRes;
}
if (isPlainObject(res)) {
const preparedRes = {};
for (const key in res) {
preparedRes[key] = prepareRes(res[key]);
}
return preparedRes;
}
if (res instanceof Error || isErrorLike(res)) {
return {
name: res.name,
message: res.message,
stack: process.env.NODE_ENV === "development" ? res.stack : void 0
};
}
return res;
};
const handleRPCRequest = async (args) => {
let clientArgs;
if ("body" in args) {
if (!args.body) {
throw new Error("Invalid request body. Only requests from an r19 client are accepted.");
}
if (!args.contentTypeHeader) {
throw new Error("Invalid Content-Type header. Only requests from an r19 client are accepted.");
}
clientArgs = await readRPCClientArgs({
contentTypeHeader: args.contentTypeHeader,
body: args.body
});
} else {
clientArgs = formDataToObject(args.formData);
if (!args.body) {
throw new Error("Invalid request body. Only requests from an r19 client are accepted.");
}
const clientArgs = decode(Buffer.from(args.body));
const procedure = findProcedure(args.procedures, clientArgs.procedurePath);
const headers = {
"Content-Type": "application/msgpack"
};
if (!procedure) {
const formData = objectToFormData({
const body = encode({
error: {

@@ -113,7 +37,4 @@ name: "RPCError",

});
const { headers, stream } = encodeFormData(formData, {
boundaryPrefix: FORM_DATA_BOUNDARY_PREFIX
});
return {
stream,
body,
headers,

@@ -125,7 +46,25 @@ statusCode: 500

try {
res = await procedure(clientArgs.procedureArgs);
res = prepareRes(res);
const procedureArgs = await replaceLeaves(clientArgs.procedureArgs, async (value) => {
if (value instanceof ArrayBuffer) {
return Buffer.from(value);
}
return value;
});
res = await procedure(procedureArgs);
res = await replaceLeaves(res, async (value) => {
if (isErrorLike(value)) {
return {
name: value.name,
message: value.message,
stack: process.env.NODE_ENV === "development" ? value.stack : void 0
};
}
if (typeof value === "function") {
throw new Error("r19 does not support function return values.");
}
return value;
});
} catch (error) {
if (isErrorLike(error)) {
const formData = objectToFormData({
const body = encode({
error: {

@@ -136,8 +75,5 @@ name: error.name,

}
});
const { headers, stream } = encodeFormData(formData, {
boundaryPrefix: FORM_DATA_BOUNDARY_PREFIX
});
}, { ignoreUndefined: true });
return {
stream,
body,
headers,

@@ -150,10 +86,7 @@ statusCode: 500

try {
const formData = objectToFormData({
const body = encode({
data: res
});
const { headers, stream } = encodeFormData(formData, {
boundaryPrefix: FORM_DATA_BOUNDARY_PREFIX
});
}, { ignoreUndefined: true });
return {
stream,
body,
headers

@@ -164,3 +97,3 @@ };

console.error(error);
const formData = objectToFormData({
const body = encode({
error: {

@@ -171,7 +104,4 @@ name: "RPCError",

});
const { headers, stream } = encodeFormData(formData, {
boundaryPrefix: FORM_DATA_BOUNDARY_PREFIX
});
return {
stream,
body,
headers,

@@ -178,0 +108,0 @@ statusCode: 500

@@ -8,2 +8,7 @@ import { RPCMiddleware } from "./createRPCMiddleware";

};
export type ProcedureCallServerResponse = {
data?: unknown;
} | {
error: unknown;
};
export type ExtractProcedures<TRPCMiddleware extends RPCMiddleware<Procedures>> = TRPCMiddleware extends RPCMiddleware<infer TProcedures> ? TProcedures : never;

@@ -10,0 +15,0 @@ export type ErrorLike = {

{
"name": "r19",
"version": "0.1.3",
"version": "0.1.4",
"description": "Simple remote procedure calls in TypeScript",

@@ -61,13 +61,11 @@ "keywords": [

"dependencies": {
"busboy": "^1.6.0",
"devalue": "^4.2.0",
"formdata-node": "^5.0.0",
"@msgpack/msgpack": "^2.8.0",
"h3": "^1.0.2"
},
"devDependencies": {
"@size-limit/preset-small-lib": "^8.1.0",
"@size-limit/preset-small-lib": "^8.1.1",
"@types/busboy": "^1.5.0",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
"@vitest/coverage-c8": "^0.26.3",
"@typescript-eslint/eslint-plugin": "^5.48.1",
"@typescript-eslint/parser": "^5.48.1",
"@vitest/coverage-c8": "^0.27.1",
"eslint": "^8.31.0",

@@ -77,6 +75,7 @@ "eslint-config-prettier": "^8.6.0",

"eslint-plugin-tsdoc": "^0.2.17",
"msw": "^0.49.2",
"node-fetch": "^3.3.0",
"prettier": "^2.8.1",
"prettier": "^2.8.2",
"prettier-plugin-jsdoc": "^0.4.2",
"size-limit": "^8.1.0",
"size-limit": "^8.1.1",
"standard-version": "^9.5.0",

@@ -86,3 +85,3 @@ "typescript": "^4.9.4",

"vite-plugin-sdk": "^0.1.0",
"vitest": "^0.26.3"
"vitest": "^0.27.1"
},

@@ -89,0 +88,0 @@ "engines": {

@@ -1,6 +0,7 @@

import { formDataToObject } from "../lib/formDataToObject.client";
import { encode, decode } from "@msgpack/msgpack";
import { replaceLeaves } from "../lib/replaceLeaves";
import { isErrorLike } from "../lib/isErrorLike";
import { objectToFormData } from "../lib/objectToFormData.client";
import { Procedures, Procedure } from "../types";
import { Procedures, Procedure, ProcedureCallServerResponse } from "../types";

@@ -71,7 +72,12 @@ const createArbitrarilyNestedFunction = <T>(

export type ResponseLike = {
formData(): Promise<FormData>;
arrayBuffer: () => Promise<ArrayBuffer>;
};
export type FetchLike = (
input: string,
init: { method: "POST"; body: FormData },
init: {
method: "POST";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
body: any;
headers: Record<string, string>;
},
) => Promise<ResponseLike>;

@@ -91,18 +97,39 @@

return createArbitrarilyNestedFunction(async (path, fnArgs) => {
const body = objectToFormData({
procedurePath: path,
procedureArgs: fnArgs[0],
});
const preparedProcedureArgs = await replaceLeaves(
fnArgs[0],
async (value) => {
if (value instanceof Blob) {
return new Uint8Array(await value.arrayBuffer());
}
if (typeof value === "function") {
throw new Error("r19 does not support function arguments.");
}
return value;
},
);
const body = encode(
{
procedurePath: path,
procedureArgs: preparedProcedureArgs,
},
{ ignoreUndefined: true },
);
const res = await resolvedFetch(args.serverURL, {
method: "POST",
body,
headers: {
"Content-Type": "application/msgpack",
},
});
const formData = await res.formData();
const resObject = formDataToObject(formData);
const arrayBuffer = await res.arrayBuffer();
const resObject = decode(
new Uint8Array(arrayBuffer),
) as ProcedureCallServerResponse;
if ("data" in resObject) {
return resObject.data;
} else {
if ("error" in resObject) {
const resError = resObject.error;

@@ -124,4 +151,12 @@

}
} else {
return replaceLeaves(resObject.data, async (value) => {
if (value instanceof Uint8Array) {
return new Blob([value]);
}
return value;
});
}
});
};

@@ -6,11 +6,11 @@ import {

eventHandler,
getHeader,
NodeMiddleware,
readRawBody,
sendStream,
send,
setHeaders,
} from "h3";
import { handleRPCRequest } from "./handleRPCRequest";
import { Buffer } from "node:buffer";
import { Procedures } from "./types";
import { handleRPCRequest } from "./handleRPCRequest";

@@ -33,6 +33,7 @@ export type RPCMiddleware<TProcedures extends Procedures> = NodeMiddleware & {

eventHandler(async (event): Promise<void> => {
const { stream, headers, statusCode } = await handleRPCRequest({
const eventBody = await readRawBody(event, false);
const { body, headers, statusCode } = await handleRPCRequest({
procedures: args.procedures,
contentTypeHeader: getHeader(event, "Content-Type"),
body: await readRawBody(event),
body: eventBody,
});

@@ -46,3 +47,3 @@

return sendStream(event, stream);
return send(event, Buffer.from(body));
}),

@@ -49,0 +50,0 @@ );

@@ -1,13 +0,7 @@

import { Readable } from "node:stream";
import { Buffer } from "node:buffer";
import busboy from "busboy";
import { decode, encode } from "@msgpack/msgpack";
import { deserialize } from "./lib/deserialize";
import { encodeFormData } from "./lib/encodeFormData";
import { isErrorLike } from "./lib/isErrorLike";
import { objectToFormData } from "./lib/objectToFormData.server";
import { unflattenObject } from "./lib/unflattenObject";
import { formDataToObject } from "./lib/formDataToObject.client";
import { replaceLeaves } from "./lib/replaceLeaves";
import { FORM_DATA_BOUNDARY_PREFIX } from "./constants";
import { Procedure, Procedures, ProcedureCallServerArgs } from "./types";

@@ -41,116 +35,9 @@

type ParseRPCClientArgs = {
contentTypeHeader: string;
body: string;
};
const readRPCClientArgs = async (args: ParseRPCClientArgs) => {
const bb = busboy({
headers: {
"content-type": args.contentTypeHeader,
},
});
const clientArgs = {} as Record<string, unknown>;
const promise = new Promise<ProcedureCallServerArgs>((resolve, reject) => {
bb.on("file", (name, file, _info) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const chunks: any[] = [];
file.on("data", (data) => {
chunks.push(data);
});
file.on("close", () => {
clientArgs[name as keyof typeof clientArgs] = Buffer.concat(chunks);
});
});
bb.on("field", (name, value, _info) => {
clientArgs[name as keyof typeof clientArgs] = deserialize(value);
});
bb.on("close", () => {
const unflattenedArgs = unflattenObject(
clientArgs,
) as ProcedureCallServerArgs;
resolve(unflattenedArgs);
});
bb.on("error", (error) => {
reject(error);
});
});
Readable.from(args.body).pipe(bb);
return promise;
};
const isPlainObject = <Value>(
value: unknown,
): value is Record<PropertyKey, Value> => {
if (typeof value !== "object" || value === null) {
return false;
}
const prototype = Object.getPrototypeOf(value);
return (
(prototype === null ||
prototype === Object.prototype ||
Object.getPrototypeOf(prototype) === null) &&
!(Symbol.toStringTag in value) &&
!(Symbol.iterator in value)
);
};
const prepareRes = (res: unknown) => {
if (Array.isArray(res)) {
const preparedRes: unknown[] = [];
for (let i = 0; i < res.length; i++) {
preparedRes[i] = prepareRes(res[i]);
}
return preparedRes;
}
if (isPlainObject(res)) {
const preparedRes: Record<PropertyKey, unknown> = {};
for (const key in res) {
preparedRes[key] = prepareRes(res[key as keyof typeof res]);
}
return preparedRes;
}
if (res instanceof Error || isErrorLike(res)) {
return {
name: res.name,
message: res.message,
stack: process.env.NODE_ENV === "development" ? res.stack : undefined,
};
}
return res;
};
type HandleRPCRequestArgs<TProcedures extends Procedures> = {
procedures: TProcedures;
} & (
| {
contentTypeHeader: string | null | undefined;
body: string | undefined;
}
| {
formData: FormData;
}
);
body: ArrayBuffer | Buffer | undefined;
};
type HandleRPCRequestReturnType = {
stream: Readable;
body: Uint8Array;
headers: Record<string, string>;

@@ -163,29 +50,17 @@ statusCode?: number;

): Promise<HandleRPCRequestReturnType> => {
let clientArgs: ProcedureCallServerArgs;
if (!args.body) {
throw new Error(
"Invalid request body. Only requests from an r19 client are accepted.",
);
}
if ("body" in args) {
if (!args.body) {
throw new Error(
"Invalid request body. Only requests from an r19 client are accepted.",
);
}
const clientArgs = decode(Buffer.from(args.body)) as ProcedureCallServerArgs;
if (!args.contentTypeHeader) {
throw new Error(
"Invalid Content-Type header. Only requests from an r19 client are accepted.",
);
}
clientArgs = await readRPCClientArgs({
contentTypeHeader: args.contentTypeHeader,
body: args.body,
});
} else {
clientArgs = formDataToObject(args.formData) as ProcedureCallServerArgs;
}
const procedure = findProcedure(args.procedures, clientArgs.procedurePath);
const headers = {
"Content-Type": "application/msgpack",
};
if (!procedure) {
const formData = objectToFormData({
const body = encode({
error: {

@@ -199,8 +74,4 @@ name: "RPCError",

const { headers, stream } = encodeFormData(formData, {
boundaryPrefix: FORM_DATA_BOUNDARY_PREFIX,
});
return {
stream,
body,
headers,

@@ -214,22 +85,47 @@ statusCode: 500,

try {
res = await procedure(clientArgs.procedureArgs);
const procedureArgs = await replaceLeaves(
clientArgs.procedureArgs,
async (value) => {
if (value instanceof ArrayBuffer) {
return Buffer.from(value);
}
res = prepareRes(res);
return value;
},
);
res = await procedure(procedureArgs);
res = await replaceLeaves(res, async (value) => {
if (isErrorLike(value)) {
return {
name: value.name,
message: value.message,
stack:
process.env.NODE_ENV === "development" ? value.stack : undefined,
};
}
if (typeof value === "function") {
throw new Error("r19 does not support function return values.");
}
return value;
});
} catch (error) {
if (isErrorLike(error)) {
const formData = objectToFormData({
error: {
name: error.name,
message: error.message,
stack:
process.env.NODE_ENV === "development" ? error.stack : undefined,
const body = encode(
{
error: {
name: error.name,
message: error.message,
stack:
process.env.NODE_ENV === "development" ? error.stack : undefined,
},
},
});
{ ignoreUndefined: true },
);
const { headers, stream } = encodeFormData(formData, {
boundaryPrefix: FORM_DATA_BOUNDARY_PREFIX,
});
return {
stream,
body,
headers,

@@ -244,12 +140,11 @@ statusCode: 500,

try {
const formData = objectToFormData({
data: res,
});
const body = encode(
{
data: res,
},
{ ignoreUndefined: true },
);
const { headers, stream } = encodeFormData(formData, {
boundaryPrefix: FORM_DATA_BOUNDARY_PREFIX,
});
return {
stream,
body,
headers,

@@ -261,3 +156,3 @@ };

const formData = objectToFormData({
const body = encode({
error: {

@@ -270,8 +165,4 @@ name: "RPCError",

const { headers, stream } = encodeFormData(formData, {
boundaryPrefix: FORM_DATA_BOUNDARY_PREFIX,
});
return {
stream,
body,
headers,

@@ -278,0 +169,0 @@ statusCode: 500,

@@ -95,3 +95,3 @@ type AppendDotPathSegment<

key,
]) as typeof res[keyof typeof res];
]) as (typeof res)[keyof typeof res];
}

@@ -98,0 +98,0 @@ }

@@ -38,2 +38,6 @@ import { RPCMiddleware } from "./createRPCMiddleware";

export type ProcedureCallServerResponse =
| { data?: unknown }
| { error: unknown };
export type ExtractProcedures<

@@ -40,0 +44,0 @@ // eslint-disable-next-line @typescript-eslint/no-explicit-any

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

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