@orpc/server
Version:
<div align="center"> <image align="center" src="https://orpc.dev/logo.webp" width=280 alt="oRPC logo" /> </div>
181 lines (174 loc) • 7.04 kB
JavaScript
import { ORPCError } from '@orpc/client';
import { toArray, intercept, resolveMaybeOptionalOptions } from '@orpc/shared';
import { toStandardLazyRequest, toFetchResponse } from '@orpc/standard-server-fetch';
import { r as resolveFriendlyStandardHandleOptions } from '../../shared/server.DZ5BIITo.mjs';
import '@orpc/standard-server';
import '@orpc/contract';
import { C as CompositeStandardHandlerPlugin, b as StandardRPCHandler } from '../../shared/server.Bxx6tqNe.mjs';
import '@orpc/client/standard';
import '@orpc/standard-server/batch';
import { S as StrictGetMethodPlugin } from '../../shared/server.TEVCLCFC.mjs';
import '../../shared/server.Ds4HPpvH.mjs';
class BodyLimitPlugin {
maxBodySize;
constructor(options) {
this.maxBodySize = options.maxBodySize;
}
initRuntimeAdapter(options) {
options.adapterInterceptors ??= [];
options.adapterInterceptors.push(async (options2) => {
if (!options2.request.body) {
return options2.next();
}
let currentBodySize = 0;
const rawReader = options2.request.body.getReader();
const reader = new ReadableStream({
start: async (controller) => {
try {
if (Number(options2.request.headers.get("content-length")) > this.maxBodySize) {
controller.error(new ORPCError("PAYLOAD_TOO_LARGE"));
return;
}
while (true) {
const { done, value } = await rawReader.read();
if (done) {
break;
}
currentBodySize += value.length;
if (currentBodySize > this.maxBodySize) {
controller.error(new ORPCError("PAYLOAD_TOO_LARGE"));
break;
}
controller.enqueue(value);
}
} finally {
controller.close();
}
}
});
const requestInit = { body: reader, duplex: "half" };
return options2.next({
...options2,
request: new Request(options2.request, requestInit)
});
});
}
}
const ORDERED_SUPPORTED_ENCODINGS = ["gzip", "deflate"];
class CompressionPlugin {
encodings;
threshold;
filter;
constructor(options = {}) {
this.encodings = options.encodings ?? ORDERED_SUPPORTED_ENCODINGS;
this.threshold = options.threshold ?? 1024;
this.filter = (request, response) => {
const hasContentDisposition = response.headers.has("content-disposition");
const contentType = response.headers.get("content-type");
if (!hasContentDisposition && contentType?.startsWith("text/event-stream")) {
return false;
}
return options.filter ? options.filter(request, response) : isCompressibleContentType(contentType);
};
}
initRuntimeAdapter(options) {
options.adapterInterceptors ??= [];
options.adapterInterceptors.unshift(async (options2) => {
const result = await options2.next();
if (!result.matched) {
return result;
}
const response = result.response;
if (response.headers.has("content-encoding") || response.headers.has("transfer-encoding") || isNoTransformCacheControl(response.headers.get("cache-control"))) {
return result;
}
const contentLength = response.headers.get("content-length");
if (contentLength && Number(contentLength) < this.threshold) {
return result;
}
const acceptEncoding = options2.request.headers.get("accept-encoding")?.split(",").map((enc) => enc.trim().split(";")[0]);
const encoding = this.encodings.find((enc) => acceptEncoding?.includes(enc));
if (!response.body || encoding === void 0) {
return result;
}
if (!this.filter(options2.request, response)) {
return result;
}
const compressedBody = response.body.pipeThrough(new CompressionStream(encoding));
const compressedHeaders = new Headers(response.headers);
compressedHeaders.delete("content-length");
compressedHeaders.set("content-encoding", encoding);
return {
...result,
response: new Response(compressedBody, {
status: response.status,
statusText: response.statusText,
headers: compressedHeaders
})
};
});
}
}
const COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/(?!event-stream(?:[;\s]|$))[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
function isCompressibleContentType(contentType) {
if (contentType === null) {
return false;
}
return COMPRESSIBLE_CONTENT_TYPE_REGEX.test(contentType);
}
const CACHE_CONTROL_NO_TRANSFORM_REGEX = /(?:^|,)\s*no-transform\s*(?:,|$)/i;
function isNoTransformCacheControl(cacheControl) {
if (cacheControl === null) {
return false;
}
return CACHE_CONTROL_NO_TRANSFORM_REGEX.test(cacheControl);
}
class CompositeFetchHandlerPlugin extends CompositeStandardHandlerPlugin {
initRuntimeAdapter(options) {
for (const plugin of this.plugins) {
plugin.initRuntimeAdapter?.(options);
}
}
}
class FetchHandler {
constructor(standardHandler, options = {}) {
this.standardHandler = standardHandler;
const plugin = new CompositeFetchHandlerPlugin(options.plugins);
plugin.initRuntimeAdapter(options);
this.adapterInterceptors = toArray(options.adapterInterceptors);
this.toFetchResponseOptions = options;
}
toFetchResponseOptions;
adapterInterceptors;
async handle(request, ...rest) {
return intercept(
this.adapterInterceptors,
{
...resolveFriendlyStandardHandleOptions(resolveMaybeOptionalOptions(rest)),
request,
toFetchResponseOptions: this.toFetchResponseOptions
},
async ({ request: request2, toFetchResponseOptions, ...options }) => {
const standardRequest = toStandardLazyRequest(request2);
const result = await this.standardHandler.handle(standardRequest, options);
if (!result.matched) {
return result;
}
return {
matched: true,
response: toFetchResponse(result.response, toFetchResponseOptions)
};
}
);
}
}
class RPCHandler extends FetchHandler {
constructor(router, options = {}) {
if (options.strictGetMethodPluginEnabled ?? true) {
options.plugins ??= [];
options.plugins.push(new StrictGetMethodPlugin());
}
super(new StandardRPCHandler(router, options), options);
}
}
export { BodyLimitPlugin, CompositeFetchHandlerPlugin, CompressionPlugin, FetchHandler, RPCHandler };