🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@orpc/client

Package Overview
Dependencies
Maintainers
1
Versions
1117
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@orpc/client - npm Package Compare versions

Comparing version
2.0.0-beta.15
to
2.0.0-beta.16
+26
-1
dist/adapters/fetch/index.mjs

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

import { sortPlugins, value, intercept } from '@orpc/shared';
import { sortPlugins, value, intercept, once } from '@orpc/shared';
import { toFetchBody, toFetchHeaders, toStandardLazyResponse } from '@standardserver/fetch';

@@ -24,2 +24,21 @@ import { S as StandardLink, R as RPCLinkCodec } from '../../shared/client.BRJnOJ0R.mjs';

const GET_SUPPORTED_DUPLEX_MODE = once(() => {
try {
let duplex;
void new Request(
"https://example.com",
{
method: "POST",
body: new ReadableStream(),
get duplex() {
duplex = "half";
return "half";
}
}
);
return duplex;
} catch {
return void 0;
}
});
class FetchLinkTransport {

@@ -51,2 +70,8 @@ origin;

};
if (body instanceof ReadableStream) {
const duplex = GET_SUPPORTED_DUPLEX_MODE();
if (duplex !== void 0) {
init.duplex = duplex;
}
}
const response = await intercept(

@@ -53,0 +78,0 @@ this.fetchInterceptors,

@@ -139,2 +139,57 @@ import { Value, Promisable } from '@orpc/shared';

interface RequestCompressionLinkPluginOptions<_T extends ClientContext> {
/**
* The compression scheme to use for request compression.
*
* @default 'gzip'
*/
encoding?: 'gzip' | 'deflate' | 'deflate-raw';
/**
* The minimum request size in bytes required to trigger compression.
* Requests smaller than this threshold will not be compressed to avoid overhead.
* If the request size cannot be determined, compression will still be applied.
*
* @default 1024 (1KB)
*/
threshold?: number;
}
declare class RequestCompressionLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
name: string;
/**
* Compression should be done after batching, to compress the final request
*/
after: string[];
private readonly encoding;
private readonly threshold;
constructor(options?: RequestCompressionLinkPluginOptions<T>);
init(options: StandardLinkOptions<T>): StandardLinkOptions<T>;
}
interface ResponseCompressionLinkPluginOptions<_T extends ClientContext> {
/**
* Compression schemes to advertise via Accept-Encoding, in preference order.
* Only schemes that can be decompressed by this plugin should be listed.
*
* @default ['gzip', 'deflate']
*/
encodings?: readonly ('gzip' | 'deflate' | 'deflate-raw')[];
}
/**
* Advertises Accept-Encoding on requests and decompresses response bodies
* based on the Content-Encoding header. Works at the standard link level,
* so it supports all adapters.
*
* @see {@link https://orpc.dev/docs/plugins/response-compression Response Compression Plugin Docs}
*/
declare class ResponseCompressionLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
name: string;
/**
* Decompression should wrap the final batch response instead of sub-responses.
*/
after: string[];
private readonly encodings;
constructor(options?: ResponseCompressionLinkPluginOptions<T>);
init(options: StandardLinkOptions<T>): StandardLinkOptions<T>;
}
interface RetryLinkPluginAttemptOptions<T extends RetryLinkPluginContext> extends StandardLinkInterceptorOptions<T> {

@@ -240,3 +295,3 @@ /**

export { BatchLinkPlugin, BatchLinkPluginError, DedupeLinkPlugin, RetryAfterLinkPlugin, RetryLinkPlugin };
export type { BatchLinkPluginGroup, BatchLinkPluginMode, BatchLinkPluginOptions, DedupeLinkPluginGroup, DedupeLinkPluginOptions, RetryAfterLinkPluginOptions, RetryLinkPluginAttemptOptions, RetryLinkPluginContext, RetryLinkPluginOptions };
export { BatchLinkPlugin, BatchLinkPluginError, DedupeLinkPlugin, RequestCompressionLinkPlugin, ResponseCompressionLinkPlugin, RetryAfterLinkPlugin, RetryLinkPlugin };
export type { BatchLinkPluginGroup, BatchLinkPluginMode, BatchLinkPluginOptions, DedupeLinkPluginGroup, DedupeLinkPluginOptions, RequestCompressionLinkPluginOptions, ResponseCompressionLinkPluginOptions, RetryAfterLinkPluginOptions, RetryLinkPluginAttemptOptions, RetryLinkPluginContext, RetryLinkPluginOptions };

@@ -139,2 +139,57 @@ import { Value, Promisable } from '@orpc/shared';

interface RequestCompressionLinkPluginOptions<_T extends ClientContext> {
/**
* The compression scheme to use for request compression.
*
* @default 'gzip'
*/
encoding?: 'gzip' | 'deflate' | 'deflate-raw';
/**
* The minimum request size in bytes required to trigger compression.
* Requests smaller than this threshold will not be compressed to avoid overhead.
* If the request size cannot be determined, compression will still be applied.
*
* @default 1024 (1KB)
*/
threshold?: number;
}
declare class RequestCompressionLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
name: string;
/**
* Compression should be done after batching, to compress the final request
*/
after: string[];
private readonly encoding;
private readonly threshold;
constructor(options?: RequestCompressionLinkPluginOptions<T>);
init(options: StandardLinkOptions<T>): StandardLinkOptions<T>;
}
interface ResponseCompressionLinkPluginOptions<_T extends ClientContext> {
/**
* Compression schemes to advertise via Accept-Encoding, in preference order.
* Only schemes that can be decompressed by this plugin should be listed.
*
* @default ['gzip', 'deflate']
*/
encodings?: readonly ('gzip' | 'deflate' | 'deflate-raw')[];
}
/**
* Advertises Accept-Encoding on requests and decompresses response bodies
* based on the Content-Encoding header. Works at the standard link level,
* so it supports all adapters.
*
* @see {@link https://orpc.dev/docs/plugins/response-compression Response Compression Plugin Docs}
*/
declare class ResponseCompressionLinkPlugin<T extends ClientContext> implements StandardLinkPlugin<T> {
name: string;
/**
* Decompression should wrap the final batch response instead of sub-responses.
*/
after: string[];
private readonly encodings;
constructor(options?: ResponseCompressionLinkPluginOptions<T>);
init(options: StandardLinkOptions<T>): StandardLinkOptions<T>;
}
interface RetryLinkPluginAttemptOptions<T extends RetryLinkPluginContext> extends StandardLinkInterceptorOptions<T> {

@@ -240,3 +295,3 @@ /**

export { BatchLinkPlugin, BatchLinkPluginError, DedupeLinkPlugin, RetryAfterLinkPlugin, RetryLinkPlugin };
export type { BatchLinkPluginGroup, BatchLinkPluginMode, BatchLinkPluginOptions, DedupeLinkPluginGroup, DedupeLinkPluginOptions, RetryAfterLinkPluginOptions, RetryLinkPluginAttemptOptions, RetryLinkPluginContext, RetryLinkPluginOptions };
export { BatchLinkPlugin, BatchLinkPluginError, DedupeLinkPlugin, RequestCompressionLinkPlugin, ResponseCompressionLinkPlugin, RetryAfterLinkPlugin, RetryLinkPlugin };
export type { BatchLinkPluginGroup, BatchLinkPluginMode, BatchLinkPluginOptions, DedupeLinkPluginGroup, DedupeLinkPluginOptions, RequestCompressionLinkPluginOptions, ResponseCompressionLinkPluginOptions, RetryAfterLinkPluginOptions, RetryLinkPluginAttemptOptions, RetryLinkPluginContext, RetryLinkPluginOptions };

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

import { toArray, value, splitInHalf, stringifyJSON, isAsyncIteratorObject, defer, loadBytes, allAbortSignal, replicateAsyncIterator, replicateReadableStream, override, AsyncIteratorClass, sleep } from '@orpc/shared';
import { parseStandardUrl, getEventMeta, flattenStandardHeader } from '@standardserver/core';
import { toArray, value, splitInHalf, stringifyJSON, isAsyncIteratorObject, defer, loadBytes, allAbortSignal, replicateAsyncIterator, replicateReadableStream, isCompressibleContentType, override, AsyncIteratorClass, sleep } from '@orpc/shared';
import { parseStandardUrl, flattenStandardHeader, generateContentDisposition, getEventMeta } from '@standardserver/core';
import { ClientPeer, isServerPeerSendMessage, decodePeerMessage } from '@standardserver/peer';
import { toFetchHeaders, toStandardBody } from '@standardserver/fetch';
import { C as COMMON_ERROR_STATUS_MAP } from '../shared/client.Dnfj8jnT.mjs';

@@ -390,2 +391,225 @@

const AVG_BYTES_PER_CHAR = 1.2;
class RequestCompressionLinkPlugin {
name = "~request-compression";
/**
* Compression should be done after batching, to compress the final request
*/
after = ["~batch"];
encoding;
threshold;
constructor(options = {}) {
this.encoding = options.encoding ?? "gzip";
this.threshold = options.threshold ?? 1024;
}
init(options) {
const transportInterceptor = async ({ next, ...interceptorOptions }) => {
const request = interceptorOptions.request;
const contentEncoding = flattenStandardHeader(request.headers["content-encoding"])?.trim()?.toLowerCase();
if (contentEncoding !== void 0) {
return next();
}
if (request.body instanceof ReadableStream) {
const contentLength = Number(flattenStandardHeader(request.headers["content-length"]));
if ((!Number.isFinite(contentLength) || contentLength >= this.threshold) && isCompressibleContentType(flattenStandardHeader(request.headers["content-type"]))) {
const compressedStream = request.body.pipeThrough(new CompressionStream(this.encoding));
return next({
...interceptorOptions,
request: {
...interceptorOptions.request,
body: compressedStream,
headers: {
...request.headers,
"standard-server": "octet-stream",
"content-length": [],
"content-encoding": this.encoding
}
}
});
}
} else if (request.body instanceof Blob) {
if ((!Number.isFinite(request.body.size) || request.body.size >= this.threshold) && isCompressibleContentType(request.body.type)) {
const compressedStream = request.body.stream().pipeThrough(new CompressionStream(this.encoding));
const contentDisposition = request.headers["content-disposition"] ?? generateContentDisposition(
request.body instanceof File ? request.body.name : "blob"
);
return next({
...interceptorOptions,
request: {
...interceptorOptions.request,
body: compressedStream,
headers: {
...request.headers,
"standard-server": "file",
"content-type": request.body.type,
"content-length": [],
"content-disposition": contentDisposition,
"content-encoding": this.encoding
}
}
});
}
} else if (request.body instanceof FormData) {
const PART_OVERHEAD = 64;
let contentLength = 0;
for (const [key, value] of request.body) {
contentLength += PART_OVERHEAD + key.length;
if (value instanceof Blob) {
if (!Number.isFinite(value.size)) {
if (!isCompressibleContentType(value.type)) {
contentLength = -Infinity;
break;
}
contentLength = Infinity;
} else {
contentLength += isCompressibleContentType(value.type) ? value.size : -value.size;
}
} else {
contentLength += value.length * AVG_BYTES_PER_CHAR;
}
}
if (contentLength >= this.threshold) {
const res = new Response(request.body);
const compressedStream = res.body.pipeThrough(new CompressionStream(this.encoding));
return next({
...interceptorOptions,
request: {
...interceptorOptions.request,
body: compressedStream,
headers: {
...request.headers,
"standard-server": [],
"content-type": res.headers.get("content-type"),
"content-length": [],
"content-encoding": this.encoding
}
}
});
}
} else if (request.body instanceof URLSearchParams) {
const string = request.body.toString();
if (string.length * AVG_BYTES_PER_CHAR >= this.threshold) {
const compressedStream = new Blob([string]).stream().pipeThrough(new CompressionStream(this.encoding));
return next({
...interceptorOptions,
request: {
...interceptorOptions.request,
body: compressedStream,
headers: {
...request.headers,
"standard-server": [],
"content-type": "application/x-www-form-urlencoded",
"content-length": [],
"content-encoding": this.encoding
}
}
});
}
} else if (request.body !== void 0 && !isAsyncIteratorObject(request.body)) {
const string = stringifyJSON(request.body);
if (string.length * AVG_BYTES_PER_CHAR >= this.threshold) {
const compressedStream = new Blob([string]).stream().pipeThrough(new CompressionStream(this.encoding));
return next({
...interceptorOptions,
request: {
...interceptorOptions.request,
body: compressedStream,
headers: {
...request.headers,
"standard-server": [],
"content-type": "application/json",
"content-length": [],
"content-encoding": this.encoding
}
}
});
}
}
return next();
};
return {
...options,
transportInterceptors: [
...toArray(options.transportInterceptors),
transportInterceptor
]
};
}
}
class ResponseCompressionLinkPlugin {
name = "~response-compression";
/**
* Decompression should wrap the final batch response instead of sub-responses.
*/
after = ["~batch"];
encodings;
constructor(options = {}) {
this.encodings = options.encodings ?? ["gzip", "deflate"];
}
init(options) {
const acceptEncodingHeader = this.encodings.join(", ");
const transportInterceptor = async ({ next, ...interceptorOptions }) => {
const response = await next({
...interceptorOptions,
request: {
...interceptorOptions.request,
headers: {
...interceptorOptions.request.headers,
"accept-encoding": acceptEncodingHeader
}
}
});
const encodings = parseContentEncodings(
flattenStandardHeader(response.headers["content-encoding"])
);
if (encodings.length === 0 || !encodings.every(isSupportedEncoding)) {
return response;
}
const decompressedHeaders = {
...response.headers,
"content-length": void 0,
"content-encoding": void 0
};
return {
...response,
headers: decompressedHeaders,
async resolveBody(hint) {
const stream = await response.resolveBody("octet-stream");
if (!(stream instanceof ReadableStream)) {
return stream;
}
let decompressedStream = stream;
for (let i = encodings.length - 1; i >= 0; i--) {
decompressedStream = decompressedStream.pipeThrough(
new DecompressionStream(encodings[i])
);
}
const fetchResponse = new Response(decompressedStream, {
headers: toFetchHeaders(decompressedHeaders)
});
return toStandardBody(fetchResponse, { hint });
}
};
};
return {
...options,
transportInterceptors: [
...toArray(options.transportInterceptors),
transportInterceptor
]
};
}
}
const SUPPORTED_ENCODINGS = ["gzip", "deflate", "deflate-raw"];
function isSupportedEncoding(encoding) {
return SUPPORTED_ENCODINGS.includes(encoding);
}
function parseContentEncodings(header) {
if (header === void 0) {
return [];
}
return header.split(",").map((part) => part.trim().toLowerCase());
}
class RetryLinkPlugin {

@@ -573,2 +797,2 @@ defaultRetry;

export { BatchLinkPlugin, BatchLinkPluginError, DedupeLinkPlugin, RetryAfterLinkPlugin, RetryLinkPlugin };
export { BatchLinkPlugin, BatchLinkPluginError, DedupeLinkPlugin, RequestCompressionLinkPlugin, ResponseCompressionLinkPlugin, RetryAfterLinkPlugin, RetryLinkPlugin };
+5
-5
{
"name": "@orpc/client",
"type": "module",
"version": "2.0.0-beta.15",
"version": "2.0.0-beta.16",
"license": "MIT",

@@ -53,6 +53,6 @@ "homepage": "https://orpc.dev",

"dependencies": {
"@standardserver/core": "^0.0.32",
"@standardserver/fetch": "^0.0.32",
"@standardserver/peer": "^0.0.32",
"@orpc/shared": "2.0.0-beta.15"
"@standardserver/core": "^0.2.0",
"@standardserver/fetch": "^0.2.0",
"@standardserver/peer": "^0.2.0",
"@orpc/shared": "2.0.0-beta.16"
},

@@ -59,0 +59,0 @@ "devDependencies": {

@@ -178,3 +178,3 @@ <h1 align="center">oRPC - Typesafe APIs Made Simple 🪄</h1>

<a href="https://github.com/Scrumplex?ref=orpc" target="_blank" rel="noopener" title="Sefa Eyeoglu"><img src="https://avatars.githubusercontent.com/u/11587657?u=ab503582165c0bbff0cca47ce31c9450bb1553c9&amp;v=4" width="32" height="32" alt="Sefa Eyeoglu" /></a>
<a href="https://github.com/nattstack?ref=orpc" target="_blank" rel="noopener" title="Natt"><img src="https://avatars.githubusercontent.com/u/31426677?u=fa9dbb8b3e66eb0ea3c88db5dc07f31c8c5418fe&amp;v=4" width="32" height="32" alt="Natt" /></a>
<a href="https://github.com/nattstack?ref=orpc" target="_blank" rel="noopener" title="NΛTT"><img src="https://avatars.githubusercontent.com/u/31426677?u=fa9dbb8b3e66eb0ea3c88db5dc07f31c8c5418fe&amp;v=4" width="32" height="32" alt="NΛTT" /></a>
<a href="https://github.com/ChromeGG?ref=orpc" target="_blank" rel="noopener" title="Adam Tkaczyk"><img src="https://avatars.githubusercontent.com/u/39050595?u=a58ca6042a6950e94e6e92442db76ef584279bc0&amp;v=4" width="32" height="32" alt="Adam Tkaczyk" /></a>

@@ -181,0 +181,0 @@ <a href="https://github.com/plancraft?ref=orpc" target="_blank" rel="noopener" title="plancraft"><img src="https://avatars.githubusercontent.com/u/46482287?v=4" width="32" height="32" alt="plancraft" /></a>