@orpc/standard-server
Version:
<div align="center"> <image align="center" src="https://orpc.unnoq.com/logo.webp" width=280 alt="oRPC logo" /> </div>
219 lines (211 loc) • 6.5 kB
JavaScript
import { isTypescriptObject, toArray } from '@orpc/shared';
class EventEncoderError extends TypeError {
}
class EventDecoderError extends TypeError {
}
class ErrorEvent extends Error {
data;
constructor(options) {
super(options?.message ?? "An error event was received", options);
this.data = options?.data;
}
}
function decodeEventMessage(encoded) {
const lines = encoded.replace(/\n+$/, "").split(/\n/);
const message = {
data: void 0,
event: void 0,
id: void 0,
retry: void 0,
comments: []
};
for (const line of lines) {
const index = line.indexOf(":");
const key = index === -1 ? line : line.slice(0, index);
const value = index === -1 ? "" : line.slice(index + 1).replace(/^\s/, "");
if (index === 0) {
message.comments.push(value);
} else if (key === "data") {
message.data ??= "";
message.data += `${value}
`;
} else if (key === "event") {
message.event = value;
} else if (key === "id") {
message.id = value;
} else if (key === "retry") {
const maybeInteger = Number.parseInt(value);
if (Number.isInteger(maybeInteger) && maybeInteger >= 0 && maybeInteger.toString() === value) {
message.retry = maybeInteger;
}
}
}
message.data = message.data?.replace(/\n$/, "");
return message;
}
class EventDecoder {
constructor(options = {}) {
this.options = options;
}
incomplete = "";
feed(chunk) {
this.incomplete += chunk;
const lastCompleteIndex = this.incomplete.lastIndexOf("\n\n");
if (lastCompleteIndex === -1) {
return;
}
const completes = this.incomplete.slice(0, lastCompleteIndex).split(/\n\n/);
this.incomplete = this.incomplete.slice(lastCompleteIndex + 2);
for (const encoded of completes) {
const message = decodeEventMessage(`${encoded}
`);
if (this.options.onEvent) {
this.options.onEvent(message);
}
}
this.incomplete = "";
}
end() {
if (this.incomplete) {
throw new EventDecoderError("Event Iterator ended before complete");
}
}
}
class EventDecoderStream extends TransformStream {
constructor() {
let decoder;
super({
start(controller) {
decoder = new EventDecoder({
onEvent: (event) => {
controller.enqueue(event);
}
});
},
transform(chunk) {
decoder.feed(chunk);
},
flush() {
decoder.end();
}
});
}
}
function assertEventId(id) {
if (id.includes("\n")) {
throw new EventEncoderError("Event's id must not contain a newline character");
}
}
function assertEventName(event) {
if (event.includes("\n")) {
throw new EventEncoderError("Event's event must not contain a newline character");
}
}
function assertEventRetry(retry) {
if (!Number.isInteger(retry) || retry < 0) {
throw new EventEncoderError("Event's retry must be a integer and >= 0");
}
}
function assertEventComment(comment) {
if (comment.includes("\n")) {
throw new EventEncoderError("Event's comment must not contain a newline character");
}
}
function encodeEventData(data) {
const lines = data?.split(/\n/) ?? [];
let output = "";
for (const line of lines) {
output += `data: ${line}
`;
}
return output;
}
function encodeEventComments(comments) {
let output = "";
for (const comment of comments ?? []) {
assertEventComment(comment);
output += `: ${comment}
`;
}
return output;
}
function encodeEventMessage(message) {
let output = "";
output += encodeEventComments(message.comments);
if (message.event !== void 0) {
assertEventName(message.event);
output += `event: ${message.event}
`;
}
if (message.retry !== void 0) {
assertEventRetry(message.retry);
output += `retry: ${message.retry}
`;
}
if (message.id !== void 0) {
assertEventId(message.id);
output += `id: ${message.id}
`;
}
output += encodeEventData(message.data);
output += "\n";
return output;
}
const EVENT_SOURCE_META_SYMBOL = Symbol("ORPC_EVENT_SOURCE_META");
function withEventMeta(container, meta) {
if (meta.id !== void 0) {
assertEventId(meta.id);
}
if (meta.retry !== void 0) {
assertEventRetry(meta.retry);
}
if (meta.comments !== void 0) {
for (const comment of meta.comments) {
assertEventComment(comment);
}
}
return new Proxy(container, {
get(target, prop, receiver) {
if (prop === EVENT_SOURCE_META_SYMBOL) {
return meta;
}
return Reflect.get(target, prop, receiver);
}
});
}
function getEventMeta(container) {
return isTypescriptObject(container) ? Reflect.get(container, EVENT_SOURCE_META_SYMBOL) : void 0;
}
function generateContentDisposition(filename) {
const escapedFileName = filename.replace(/"/g, '\\"');
const encodedFilenameStar = encodeURIComponent(filename).replace(/['()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`).replace(/%(7C|60|5E)/g, (str, hex) => String.fromCharCode(Number.parseInt(hex, 16)));
return `inline; filename="${escapedFileName}"; filename*=utf-8''${encodedFilenameStar}`;
}
function getFilenameFromContentDisposition(contentDisposition) {
const encodedFilenameStarMatch = contentDisposition.match(/filename\*=(UTF-8'')?([^;]*)/i);
if (encodedFilenameStarMatch && typeof encodedFilenameStarMatch[2] === "string") {
return decodeURIComponent(encodedFilenameStarMatch[2]);
}
const encodedFilenameMatch = contentDisposition.match(/filename="((?:\\"|[^"])*)"/i);
if (encodedFilenameMatch && typeof encodedFilenameMatch[1] === "string") {
return encodedFilenameMatch[1].replace(/\\"/g, '"');
}
}
function mergeStandardHeaders(a, b) {
const merged = { ...a };
for (const key in b) {
if (Array.isArray(b[key])) {
merged[key] = [...toArray(merged[key]), ...b[key]];
} else if (b[key] !== void 0) {
if (Array.isArray(merged[key])) {
merged[key] = [...merged[key], b[key]];
} else if (merged[key] !== void 0) {
merged[key] = [merged[key], b[key]];
} else {
merged[key] = b[key];
}
}
}
return merged;
}
export { ErrorEvent, EventDecoder, EventDecoderError, EventDecoderStream, EventEncoderError, assertEventComment, assertEventId, assertEventName, assertEventRetry, decodeEventMessage, encodeEventComments, encodeEventData, encodeEventMessage, generateContentDisposition, getEventMeta, getFilenameFromContentDisposition, mergeStandardHeaders, withEventMeta };