@orpc/shared
Version:
<div align="center"> <image align="center" src="https://orpc.dev/logo.webp" width=280 alt="oRPC logo" /> </div>
712 lines (692 loc) • 19.6 kB
JavaScript
export { group, guard, mapEntries, mapValues, omit, retry, sleep } from 'radash';
function resolveMaybeOptionalOptions(rest) {
return rest[0] ?? {};
}
function toArray(value) {
return Array.isArray(value) ? value : value === void 0 || value === null ? [] : [value];
}
function splitInHalf(arr) {
const half = Math.ceil(arr.length / 2);
return [arr.slice(0, half), arr.slice(half)];
}
function readAsBuffer(source) {
if (typeof source.bytes === "function") {
return source.bytes();
}
return source.arrayBuffer();
}
const ORPC_NAME = "orpc";
const ORPC_SHARED_PACKAGE_NAME = "@orpc/shared";
const ORPC_SHARED_PACKAGE_VERSION = "1.14.4";
class AbortError extends Error {
constructor(...rest) {
super(...rest);
this.name = "AbortError";
}
}
function once(fn) {
let cached;
return () => {
if (cached) {
return cached.result;
}
const result = fn();
cached = { result };
return result;
};
}
function sequential(fn) {
let lastOperationPromise = Promise.resolve();
return (...args) => {
return lastOperationPromise = lastOperationPromise.catch(() => {
}).then(() => {
return fn(...args);
});
};
}
function defer(callback) {
if (typeof setTimeout === "function") {
setTimeout(callback, 0);
} else {
Promise.resolve().then(() => Promise.resolve().then(() => Promise.resolve().then(callback)));
}
}
const SPAN_ERROR_STATUS = 2;
const GLOBAL_OTEL_CONFIG_KEY = `__${ORPC_SHARED_PACKAGE_NAME}@${ORPC_SHARED_PACKAGE_VERSION}/otel/config__`;
function setGlobalOtelConfig(config) {
globalThis[GLOBAL_OTEL_CONFIG_KEY] = config;
}
function getGlobalOtelConfig() {
return globalThis[GLOBAL_OTEL_CONFIG_KEY];
}
function startSpan(name, options = {}, context) {
const tracer = getGlobalOtelConfig()?.tracer;
return tracer?.startSpan(name, options, context);
}
function setSpanError(span, error, options = {}) {
if (!span) {
return;
}
const exception = toOtelException(error);
span.recordException(exception);
if (!options.signal?.aborted || options.signal.reason !== error) {
span.setStatus({
code: SPAN_ERROR_STATUS,
message: exception.message
});
}
}
function setSpanAttribute(span, key, value) {
if (!span || value === void 0) {
return;
}
span.setAttribute(key, value);
}
function toOtelException(error) {
if (error instanceof Error) {
const exception = {
message: error.message,
name: error.name,
stack: error.stack
};
if ("code" in error && (typeof error.code === "string" || typeof error.code === "number")) {
exception.code = error.code;
}
return exception;
}
return { message: String(error) };
}
function toSpanAttributeValue(data) {
if (data === void 0) {
return "undefined";
}
try {
return JSON.stringify(data, (_, value) => {
if (typeof value === "bigint") {
return value.toString();
}
if (value instanceof Map || value instanceof Set) {
return Array.from(value);
}
return value;
});
} catch {
return String(data);
}
}
async function runWithSpan({ name, context, ...options }, fn) {
const tracer = getGlobalOtelConfig()?.tracer;
if (!tracer) {
return fn();
}
const callback = async (span) => {
try {
return await fn(span);
} catch (e) {
setSpanError(span, e, options);
throw e;
} finally {
span.end();
}
};
if (context) {
return tracer.startActiveSpan(name, options, context, callback);
} else {
return tracer.startActiveSpan(name, options, callback);
}
}
async function runInSpanContext(span, fn) {
const otelConfig = getGlobalOtelConfig();
if (!span || !otelConfig) {
return fn();
}
const ctx = otelConfig.trace.setSpan(otelConfig.context.active(), span);
return otelConfig.context.with(ctx, fn);
}
class AsyncIdQueue {
openIds = /* @__PURE__ */ new Set();
queues = /* @__PURE__ */ new Map();
waiters = /* @__PURE__ */ new Map();
get length() {
return this.openIds.size;
}
get waiterIds() {
return Array.from(this.waiters.keys());
}
hasBufferedItems(id) {
return Boolean(this.queues.get(id)?.length);
}
open(id) {
this.openIds.add(id);
}
isOpen(id) {
return this.openIds.has(id);
}
push(id, item) {
this.assertOpen(id);
const pending = this.waiters.get(id);
if (pending?.length) {
pending.shift()[0](item);
if (pending.length === 0) {
this.waiters.delete(id);
}
} else {
const items = this.queues.get(id);
if (items) {
items.push(item);
} else {
this.queues.set(id, [item]);
}
}
}
async pull(id) {
this.assertOpen(id);
const items = this.queues.get(id);
if (items?.length) {
const item = items.shift();
if (items.length === 0) {
this.queues.delete(id);
}
return item;
}
return new Promise((resolve, reject) => {
const waitingPulls = this.waiters.get(id);
const pending = [resolve, reject];
if (waitingPulls) {
waitingPulls.push(pending);
} else {
this.waiters.set(id, [pending]);
}
});
}
close({ id, reason } = {}) {
if (id === void 0) {
this.waiters.forEach((pendingPulls, id2) => {
const error2 = reason ?? new AbortError(`[AsyncIdQueue] Queue[${id2}] was closed or aborted while waiting for pulling.`);
pendingPulls.forEach(([, reject]) => reject(error2));
});
this.waiters.clear();
this.openIds.clear();
this.queues.clear();
return;
}
const error = reason ?? new AbortError(`[AsyncIdQueue] Queue[${id}] was closed or aborted while waiting for pulling.`);
this.waiters.get(id)?.forEach(([, reject]) => reject(error));
this.waiters.delete(id);
this.openIds.delete(id);
this.queues.delete(id);
}
assertOpen(id) {
if (!this.isOpen(id)) {
throw new Error(`[AsyncIdQueue] Cannot access queue[${id}] because it is not open or aborted.`);
}
}
}
function isAsyncIteratorObject(maybe) {
if (!maybe || typeof maybe !== "object") {
return false;
}
return "next" in maybe && typeof maybe.next === "function" && Symbol.asyncIterator in maybe && typeof maybe[Symbol.asyncIterator] === "function";
}
const fallbackAsyncDisposeSymbol = Symbol.for("asyncDispose");
const asyncDisposeSymbol = Symbol.asyncDispose ?? fallbackAsyncDisposeSymbol;
class AsyncIteratorClass {
#isDone = false;
#isExecuteComplete = false;
#cleanup;
#next;
constructor(next, cleanup) {
this.#cleanup = cleanup;
this.#next = sequential(async () => {
if (this.#isDone) {
return { done: true, value: void 0 };
}
try {
const result = await next();
if (result.done) {
this.#isDone = true;
}
return result;
} catch (err) {
this.#isDone = true;
throw err;
} finally {
if (this.#isDone && !this.#isExecuteComplete) {
this.#isExecuteComplete = true;
await this.#cleanup("next");
}
}
});
}
next() {
return this.#next();
}
async return(value) {
this.#isDone = true;
if (!this.#isExecuteComplete) {
this.#isExecuteComplete = true;
await this.#cleanup("return");
}
return { done: true, value };
}
async throw(err) {
this.#isDone = true;
if (!this.#isExecuteComplete) {
this.#isExecuteComplete = true;
await this.#cleanup("throw");
}
throw err;
}
/**
* asyncDispose symbol only available in esnext, we should fallback to Symbol.for('asyncDispose')
*/
async [asyncDisposeSymbol]() {
this.#isDone = true;
if (!this.#isExecuteComplete) {
this.#isExecuteComplete = true;
await this.#cleanup("dispose");
}
}
[Symbol.asyncIterator]() {
return this;
}
}
function replicateAsyncIterator(source, count) {
const queue = new AsyncIdQueue();
const ids = Array.from({ length: count }, (_, i) => i.toString());
let isSourceFinished = false;
const start = once(async () => {
try {
while (true) {
const item = await source.next();
ids.forEach((id) => {
if (queue.isOpen(id)) {
queue.push(id, { next: item });
}
});
if (item.done) {
break;
}
}
} catch (error) {
ids.forEach((id) => {
if (queue.isOpen(id)) {
queue.push(id, { error });
}
});
} finally {
isSourceFinished = true;
}
});
const replicated = ids.map((id) => {
queue.open(id);
return new AsyncIteratorClass(
async () => {
start();
const item = await queue.pull(id);
if (item.next) {
return item.next;
}
throw item.error;
},
async (reason) => {
queue.close({ id });
if (reason !== "next" && !queue.length && !isSourceFinished) {
isSourceFinished = true;
await source?.return?.();
}
}
);
});
return replicated;
}
function asyncIteratorWithSpan({ name, ...options }, iterator) {
let span;
return new AsyncIteratorClass(
async () => {
span ??= startSpan(name);
try {
const result = await runInSpanContext(span, () => iterator.next());
span?.addEvent(result.done ? "completed" : "yielded");
return result;
} catch (err) {
setSpanError(span, err, options);
throw err;
}
},
async (reason) => {
try {
if (reason !== "next") {
await runInSpanContext(span, () => iterator.return?.());
}
} catch (err) {
setSpanError(span, err, options);
throw err;
} finally {
span?.end();
}
}
);
}
class EventPublisher {
#listenersMap = /* @__PURE__ */ new Map();
#maxBufferedEvents;
constructor(options = {}) {
this.#maxBufferedEvents = options.maxBufferedEvents ?? 100;
}
get size() {
return this.#listenersMap.size;
}
/**
* Emits an event and delivers the payload to all subscribed listeners.
*/
publish(event, payload) {
const listeners = this.#listenersMap.get(event);
if (!listeners) {
return;
}
for (const listener of listeners) {
listener(payload);
}
}
subscribe(event, listenerOrOptions) {
if (typeof listenerOrOptions === "function") {
let listeners = this.#listenersMap.get(event);
if (!listeners) {
this.#listenersMap.set(event, listeners = []);
}
listeners.push(listenerOrOptions);
return once(() => {
listeners.splice(listeners.indexOf(listenerOrOptions), 1);
if (listeners.length === 0) {
this.#listenersMap.delete(event);
}
});
}
const signal = listenerOrOptions?.signal;
const maxBufferedEvents = listenerOrOptions?.maxBufferedEvents ?? this.#maxBufferedEvents;
signal?.throwIfAborted();
const bufferedEvents = [];
const pullResolvers = [];
const unsubscribe = this.subscribe(event, (payload) => {
const resolver = pullResolvers.shift();
if (resolver) {
resolver[0]({ done: false, value: payload });
} else {
bufferedEvents.push(payload);
if (bufferedEvents.length > maxBufferedEvents) {
bufferedEvents.shift();
}
}
});
const abortListener = (event2) => {
unsubscribe();
pullResolvers.forEach((resolver) => resolver[1](event2.target.reason));
pullResolvers.length = 0;
bufferedEvents.length = 0;
};
signal?.addEventListener("abort", abortListener, { once: true });
return new AsyncIteratorClass(async () => {
if (signal?.aborted) {
throw signal.reason;
}
if (bufferedEvents.length > 0) {
return { done: false, value: bufferedEvents.shift() };
}
return new Promise((resolve, reject) => {
pullResolvers.push([resolve, reject]);
});
}, async () => {
unsubscribe();
signal?.removeEventListener("abort", abortListener);
pullResolvers.forEach((resolver) => resolver[0]({ done: true, value: void 0 }));
pullResolvers.length = 0;
bufferedEvents.length = 0;
});
}
}
class SequentialIdGenerator {
index = BigInt(1);
generate() {
const id = this.index.toString(36);
this.index++;
return id;
}
}
function compareSequentialIds(a, b) {
if (a.length !== b.length) {
return a.length - b.length;
}
return a < b ? -1 : a > b ? 1 : 0;
}
function onStart(callback) {
return async (options, ...rest) => {
await callback(options, ...rest);
return await options.next();
};
}
function onSuccess(callback) {
return async (options, ...rest) => {
const result = await options.next();
await callback(result, options, ...rest);
return result;
};
}
function onError(callback) {
return async (options, ...rest) => {
try {
return await options.next();
} catch (error) {
await callback(error, options, ...rest);
throw error;
}
};
}
function onFinish(callback) {
let state;
return async (options, ...rest) => {
try {
const result = await options.next();
state = [null, result, true];
return result;
} catch (error) {
state = [error, void 0, false];
throw error;
} finally {
await callback(state, options, ...rest);
}
};
}
function intercept(interceptors, options, main) {
const next = (options2, index) => {
const interceptor = interceptors[index];
if (!interceptor) {
return main(options2);
}
return interceptor({
...options2,
next: (newOptions = options2) => next(newOptions, index + 1)
});
};
return next(options, 0);
}
function parseEmptyableJSON(text) {
if (!text) {
return void 0;
}
return JSON.parse(text);
}
function stringifyJSON(value) {
return JSON.stringify(value);
}
function findDeepMatches(check, payload, segments = [], maps = [], values = []) {
if (check(payload)) {
maps.push(segments);
values.push(payload);
} else if (Array.isArray(payload)) {
payload.forEach((v, i) => {
findDeepMatches(check, v, [...segments, i], maps, values);
});
} else if (isObject(payload)) {
for (const key in payload) {
findDeepMatches(check, payload[key], [...segments, key], maps, values);
}
}
return { maps, values };
}
function getConstructor(value) {
if (!isTypescriptObject(value)) {
return null;
}
return Object.getPrototypeOf(value)?.constructor;
}
function isObject(value) {
if (!value || typeof value !== "object") {
return false;
}
const proto = Object.getPrototypeOf(value);
return proto === Object.prototype || !proto || !proto.constructor;
}
function isTypescriptObject(value) {
return !!value && (typeof value === "object" || typeof value === "function");
}
function clone(value) {
if (Array.isArray(value)) {
return value.map(clone);
}
if (isObject(value)) {
const result = {};
for (const key in value) {
result[key] = clone(value[key]);
}
for (const sym of Object.getOwnPropertySymbols(value)) {
result[sym] = clone(value[sym]);
}
return result;
}
return value;
}
function get(object, path) {
let current = object;
for (const key of path) {
if (!isTypescriptObject(current)) {
return void 0;
}
current = current[key];
}
return current;
}
function isPropertyKey(value) {
const type = typeof value;
return type === "string" || type === "number" || type === "symbol";
}
const NullProtoObj = /* @__PURE__ */ (() => {
const e = function() {
};
e.prototype = /* @__PURE__ */ Object.create(null);
Object.freeze(e.prototype);
return e;
})();
function value(value2, ...args) {
if (typeof value2 === "function") {
return value2(...args);
}
return value2;
}
function fallback(value2, fallback2) {
return value2 === void 0 ? fallback2 : value2;
}
function preventNativeAwait(target) {
return new Proxy(target, {
get(target2, prop, receiver) {
const value2 = Reflect.get(target2, prop, receiver);
if (prop !== "then" || typeof value2 !== "function") {
return value2;
}
return new Proxy(value2, {
apply(targetFn, thisArg, args) {
if (args.length !== 2 || args.some((arg) => !isNativeFunction(arg))) {
return Reflect.apply(targetFn, thisArg, args);
}
let shouldOmit = true;
args[0].call(thisArg, preventNativeAwait(new Proxy(target2, {
get: (target3, prop2, receiver2) => {
if (shouldOmit && prop2 === "then") {
shouldOmit = false;
return void 0;
}
return Reflect.get(target3, prop2, receiver2);
}
})));
}
});
}
});
}
const NATIVE_FUNCTION_REGEX = /^\s*function\s*\(\)\s*\{\s*\[native code\]\s*\}\s*$/;
function isNativeFunction(fn) {
return typeof fn === "function" && NATIVE_FUNCTION_REGEX.test(fn.toString());
}
function overlayProxy(target, partial) {
const proxy = new Proxy(typeof target === "function" ? partial : target, {
get(_, prop) {
const targetValue = prop in partial ? partial : value(target);
const v = Reflect.get(targetValue, prop);
return typeof v === "function" ? v.bind(targetValue) : v;
},
has(_, prop) {
return Reflect.has(partial, prop) || Reflect.has(value(target), prop);
}
});
return proxy;
}
function streamToAsyncIteratorClass(stream) {
const reader = stream.getReader();
return new AsyncIteratorClass(
async () => {
return reader.read();
},
async () => {
await reader.cancel();
}
);
}
function asyncIteratorToStream(iterator) {
return new ReadableStream({
async pull(controller) {
const { done, value } = await iterator.next();
if (done) {
controller.close();
} else {
controller.enqueue(value);
}
},
async cancel() {
await iterator.return?.();
}
});
}
function asyncIteratorToUnproxiedDataStream(iterator) {
return new ReadableStream({
async pull(controller) {
const { done, value } = await iterator.next();
if (done) {
controller.close();
} else {
const unproxied = isObject(value) ? { ...value } : Array.isArray(value) ? value.map((i) => i) : value;
controller.enqueue(unproxied);
}
},
async cancel() {
await iterator.return?.();
}
});
}
function tryDecodeURIComponent(value) {
try {
return decodeURIComponent(value);
} catch {
return value;
}
}
export { AbortError, AsyncIdQueue, AsyncIteratorClass, EventPublisher, NullProtoObj, ORPC_NAME, ORPC_SHARED_PACKAGE_NAME, ORPC_SHARED_PACKAGE_VERSION, SequentialIdGenerator, asyncIteratorToStream, asyncIteratorToUnproxiedDataStream, asyncIteratorWithSpan, clone, compareSequentialIds, defer, fallback, findDeepMatches, get, getConstructor, getGlobalOtelConfig, intercept, isAsyncIteratorObject, isObject, isPropertyKey, isTypescriptObject, onError, onFinish, onStart, onSuccess, once, overlayProxy, parseEmptyableJSON, preventNativeAwait, readAsBuffer, replicateAsyncIterator, resolveMaybeOptionalOptions, runInSpanContext, runWithSpan, sequential, setGlobalOtelConfig, setSpanAttribute, setSpanError, splitInHalf, startSpan, streamToAsyncIteratorClass, stringifyJSON, toArray, toOtelException, toSpanAttributeValue, tryDecodeURIComponent, value };