zagora
Version:
A minimalist, type-safe, error-safe typed functions in 214 lines of TypeScript based on Zod v4. No batteries, just functions. Simple alternative to oRPC and tRPC.
124 lines (122 loc) • 4.15 kB
JavaScript
import { z } from "zod";
//#region src/index.ts
function zagora() {
return new FluentBuilder();
}
const za = zagora();
var FluentBuilder = class FluentBuilder {
_inputTuple = null;
_output = null;
_err = null;
input(tuple) {
const next = new FluentBuilder();
next._inputTuple = tuple;
next._output = this._output;
next._err = this._err;
return next;
}
output(schema) {
const next = new FluentBuilder();
next._inputTuple = this._inputTuple;
next._output = schema;
next._err = this._err;
return next;
}
errors(schema) {
const next = new FluentBuilder();
next._inputTuple = this._inputTuple;
next._output = this._output;
next._err = schema;
return next;
}
errorsMap(map) {
const next = new FluentBuilder();
next._inputTuple = this._inputTuple;
next._output = this._output;
next._err = map;
return next;
}
handler(impl) {
if (!this._inputTuple) throw new Error(".input(z.tuple(...)) must be called first");
if (!this._output) throw new Error(".output(...) must be called first");
const tuple = this._inputTuple;
const items = tuple?._def?.items ?? tuple?._def?.tuple ?? [];
const outputSchema = this._output;
const errSchema = this._err;
const isOmittable = (s) => {
const tn = s?._def?.typeName;
return tn === "ZodOptional" || tn === "ZodDefault";
};
let minRequired = 0;
for (let i = items.length - 1; i >= 0; --i) if (!isOmittable(items[i])) {
minRequired = i + 1;
break;
}
const tupleParsers = [];
for (let k = minRequired; k <= items.length; ++k) {
const prefix = items.slice(0, k);
tupleParsers.push(z.tuple(prefix));
}
const inputParser = tupleParsers.length === 1 ? tupleParsers[0] : z.union(tupleParsers);
const runtimeErrSchema = errSchema && !(errSchema instanceof Object && !("parse" in errSchema)) ? errSchema : void 0;
const wrapper = async (...rawArgs) => {
const parsed = inputParser.safeParse(rawArgs);
if (!parsed.success) return [null, parsed.error];
const provided = parsed.data;
const finalArgs = [...provided];
for (let i = provided.length; i < items.length; ++i) {
const p = items[i].safeParse(void 0);
if (!p.success) return [null, p.error];
finalArgs.push(p.data);
}
try {
const rawResult = await impl(...finalArgs);
if (Array.isArray(rawResult) && rawResult.length === 2) {
const [maybeOut, maybeErr] = rawResult;
if (maybeErr != null) {
if (errSchema && typeof errSchema === "object" && !("parse" in errSchema)) {
for (const k of Object.keys(errSchema)) {
const sch = errSchema[k];
const r = sch.safeParse(maybeErr);
if (r.success) return [null, r.data];
}
return [null, maybeErr instanceof Error ? maybeErr : new Error(String(maybeErr ?? "unknown"))];
}
if (runtimeErrSchema) {
const r = await runtimeErrSchema.safeParseAsync(maybeErr);
if (!r.success) return [null, r.error];
return [null, r.data];
}
return [null, maybeErr instanceof Error ? maybeErr : new Error(String(maybeErr ?? "unknown"))];
}
const po$1 = await outputSchema.safeParseAsync(maybeOut);
if (!po$1.success) return [null, po$1.error];
return [po$1.data, null];
}
const po = await outputSchema.safeParseAsync(rawResult);
if (!po.success) return [null, po.error];
return [po.data, null];
} catch (err) {
if (errSchema && typeof errSchema === "object" && !("parse" in errSchema)) {
for (const k of Object.keys(errSchema)) {
const sch = errSchema[k];
const r = sch.safeParse(err);
if (r.success) return [null, r.data];
}
return [null, err instanceof Error ? err : new Error(String(err ?? "unknown"))];
}
if (runtimeErrSchema) {
const r = await runtimeErrSchema.safeParseAsync(err);
if (!r.success) return [null, r.error];
return [null, r.data];
}
return [null, err instanceof Error ? err : new Error(String(err ?? "unknown"))];
}
};
const forwardImpl = (...args) => wrapper(...args);
const forward = forwardImpl;
return forward;
}
};
//#endregion
export { FluentBuilder, z, za, zagora };