life
Version:
Life.js is the first fullstack framework to build agentic web applications. It is minimal, extensible, and typesafe. Well, everything you love.
412 lines (407 loc) • 11.7 kB
JavaScript
import {
__name
} from "./chunk-2D3UJWOA.mjs";
// shared/error.ts
import z from "zod";
// shared/prefixed-id.ts
import { init as initCuid2, isCuid } from "@paralleldrive/cuid2";
function newId(prefix, length = 12) {
const cuid2 = initCuid2({ length });
return `${prefix}_${cuid2()}`;
}
__name(newId, "newId");
// shared/error.ts
var lifeErrorCodes = {
/**
* Used when the user sends or the server returns invalid data.
*/
Validation: {
retriable: false,
defaultMessage: "Invalid data provided.",
httpEquivalent: 400
},
/**
* Used when the user is not authorized to access a resource
*/
Forbidden: {
retriable: false,
defaultMessage: "Not allowed to access this resource.",
httpEquivalent: 403
},
/**
* Used when an operation took too long and timed out.
*/
Timeout: {
retriable: true,
defaultMessage: "Operation timed out.",
httpEquivalent: 504
},
/**
* Used when the user has exceeded the rate limit for a resource.
*/
RateLimit: {
retriable: true,
defaultMessage: "Rate limit exceeded.",
httpEquivalent: 429
},
/**
* Used when a resource was not found or missing.
*/
NotFound: {
retriable: false,
defaultMessage: "Resource not found.",
httpEquivalent: 404
},
/**
* Used when an operation is about to conflict with another.
* E.g., a version mismatch, a unique constraint violation, etc.
*/
Conflict: {
retriable: false,
defaultMessage: "Operation conflicted.",
httpEquivalent: 409
},
/**
* Used when an upstream service or resource fails.
* E.g., a database connection error, an OpenAI API downtime, etc.
*/
Upstream: {
retriable: true,
defaultMessage: "Upstream error.",
httpEquivalent: 502
},
/**
* Used when an unexpected error is thrown.
*/
Unknown: {
retriable: false,
defaultMessage: "Unknown error.",
httpEquivalent: 500
},
/**
* Used to obfuscate internal errors publicly.
* Prevents leaking sensitive informations to public consumers.
*/
Internal: {
retriable: true,
defaultMessage: "Internal error.",
httpEquivalent: 500
}
};
var LifeErrorClass = class _LifeErrorClass extends Error {
static {
__name(this, "LifeErrorClass");
}
name = "LifeError";
/**
* The unique identifier of the error.
*/
id = newId("error");
/**
* The error code.
* Can be one of:
* - Validation
* - Forbidden
* - Timeout
* - RateLimit
* - NotFound
* - Conflict
* - Upstream
* - Unknown
* - Internal
*/
code;
/**
* Additional pieces of evidence attached to the error.
*/
attributes;
/**
* Used to indicate whether the operation that caused the error can be retried.
*/
retriable;
/**
* The suggested time (in ms) to wait before retrying the operation that caused the error.
* Check `.retriable` first to ensure the operation can be retried.
*/
retryAfterMs;
/**
* The HTTP status code equivalent to the error code.
*/
httpEquivalent;
/**
* Used to indicate whether this error is public and can be safely sent to external clients.
*/
isPublic;
constructor({
code,
message,
attributes,
retryAfterMs,
cause,
isPublic = false
}) {
const definition = lifeErrorCodes[code];
super(message ?? definition.defaultMessage);
this.code = code;
this.retriable = definition.retriable;
this.attributes = attributes ?? {};
this.retryAfterMs = retryAfterMs;
this.httpEquivalent = definition.httpEquivalent;
this.isPublic = isPublic;
this.cause = cause;
if (Error.captureStackTrace) Error.captureStackTrace(this, _LifeErrorClass);
}
toJSON() {
return {
id: this.id,
code: this.code,
message: this.message,
retriable: this.retriable,
attributes: this.attributes,
retryAfterMs: this.retryAfterMs,
httpEquivalent: this.httpEquivalent,
stack: this.stack,
cause: this.cause
};
}
};
function lifeError(params) {
if (params.code === "Unknown" && isLifeError(params.cause)) {
return params.cause;
}
return new LifeErrorClass(params);
}
__name(lifeError, "lifeError");
function isLifeError(error) {
return error instanceof LifeErrorClass;
}
__name(isLifeError, "isLifeError");
var serializedLifeErrorSchema = z.object({
_isLifeError: z.literal(true),
id: z.string(),
code: z.string(),
stack: z.string().optional(),
message: z.string(),
attributes: z.object().optional(),
retryAfterMs: z.number().optional(),
isPublic: z.boolean().optional(),
cause: z.any().optional()
});
function lifeErrorToObject(error) {
if (!(error instanceof LifeErrorClass))
throw lifeError({
code: "Validation",
message: "The provided object is not a LifeError instance."
});
return {
_isLifeError: true,
id: error.id,
code: error.code,
stack: error.stack,
message: error.message,
attributes: error.attributes,
retryAfterMs: error.retryAfterMs,
cause: error.cause
};
}
__name(lifeErrorToObject, "lifeErrorToObject");
function lifeErrorFromObject(obj) {
const { success: success2, data } = serializedLifeErrorSchema.safeParse(obj);
if (!success2)
throw lifeError({
code: "Validation",
message: "The provided object is not a serialized LifeError."
});
const err = lifeError({
code: data.code,
message: data.message,
retryAfterMs: data.retryAfterMs,
attributes: data.attributes,
cause: data.cause,
isPublic: data.isPublic
});
err.id = data.id;
err.stack = data.stack;
return err;
}
__name(lifeErrorFromObject, "lifeErrorFromObject");
function obfuscateLifeError(error) {
if (process.env.NODE_ENV === "development") return error;
let publicError;
if (error.isPublic) {
publicError = lifeError({
code: error.code,
message: error.message,
attributes: error.attributes,
retryAfterMs: error.retryAfterMs,
cause: error.cause
});
} else publicError = lifeError({ code: "Internal" });
publicError.isPublic = true;
publicError.id = error.id;
publicError.stack = void 0;
return publicError;
}
__name(obfuscateLifeError, "obfuscateLifeError");
// shared/operation.ts
import z2 from "zod";
var OPERATION_RESULT = Symbol("OperationResult");
var isResult = /* @__PURE__ */ __name((value) => Array.isArray(value) && OPERATION_RESULT in value, "isResult");
var success = /* @__PURE__ */ __name((data) => {
const result = Object.assign([void 0, isResult(data) ? data[1] : data], {
[OPERATION_RESULT]: true
});
return result;
}, "success");
var failure = /* @__PURE__ */ __name((errorOrDef) => {
const error = isLifeError(errorOrDef) ? errorOrDef : lifeError(errorOrDef);
const result = Object.assign([error, void 0], {
[OPERATION_RESULT]: true
});
return result;
}, "failure");
function attempt(task) {
const handleError = /* @__PURE__ */ __name((error) => {
if (isLifeError(error)) return failure(error);
return failure({ code: "Unknown", cause: error });
}, "handleError");
const handleResult = /* @__PURE__ */ __name((result) => {
if (isResult(result)) return result;
return success(result);
}, "handleResult");
if (task instanceof Promise)
return task.then(handleResult).catch(handleError);
try {
const result = task();
if (result instanceof Promise)
return result.then(handleResult).catch(handleError);
return handleResult(result);
} catch (error) {
return handleError(error);
}
}
__name(attempt, "attempt");
var dataOrThrow = /* @__PURE__ */ __name((result) => {
if (!isResult(result)) return result;
const [error, data] = result;
if (error) throw error;
return data;
}, "dataOrThrow");
var functionToPublic = /* @__PURE__ */ __name((func) => {
const unsafeFunc = /* @__PURE__ */ __name((...args) => {
try {
const result = func(...args);
if (result instanceof Promise) {
return result.then((awaitedResult) => {
if (isResult(awaitedResult)) {
const [errAsync, dataAsync] = awaitedResult;
if (errAsync) throw errAsync;
return dataAsync;
}
return awaitedResult;
}).catch((error) => {
throw error;
});
}
if (isResult(result)) {
const [errorSync, dataSync] = result;
if (errorSync) throw errorSync;
return dataSync;
}
return result;
} catch (error) {
if (error instanceof Error) throw error;
throw error;
}
}, "unsafeFunc");
return unsafeFunc;
}, "functionToPublic");
var instanceToPublic = /* @__PURE__ */ __name((instance) => {
const wrappedCache = /* @__PURE__ */ new WeakMap();
const createProxy = /* @__PURE__ */ __name((target) => {
if (wrappedCache.has(target)) return wrappedCache.get(target);
const proxy = new Proxy(target, {
get(innerTarget, prop) {
if (prop === "safe") return innerTarget;
const value = innerTarget[prop];
if (typeof value === "function") {
return functionToPublic(value.bind(innerTarget));
}
if (value !== null && typeof value === "object") {
const shouldSkip = value instanceof Date || value instanceof RegExp || value instanceof Promise || Array.isArray(value) || value instanceof Map || value instanceof Set || value instanceof WeakMap || value instanceof WeakSet || ArrayBuffer.isView(value);
if (shouldSkip) return value;
return createProxy(value);
}
return value;
}
});
wrappedCache.set(target, proxy);
return proxy;
}, "createProxy");
return createProxy(instance);
}, "instanceToPublic");
var classToPublic = /* @__PURE__ */ __name((InternalClass) => new Proxy(InternalClass, {
construct(target, args) {
const instance = new target(...args);
return instanceToPublic(instance);
}
}), "classToPublic");
function toPublic(input) {
if (typeof input === "function" && input.prototype && input.prototype.constructor === input) {
return classToPublic(input);
}
if (typeof input === "function") {
return functionToPublic(input);
}
if (typeof input === "object" && input !== null) {
return instanceToPublic(input);
}
return input;
}
__name(toPublic, "toPublic");
var resultSchema = z2.tuple([z2.null().or(z2.undefined()), z2.unknown()]).or(z2.tuple([z2.instanceof(LifeErrorClass), z2.null().or(z2.undefined())])).transform((val) => {
const [error, data] = val;
if (error) return failure(error);
return success(data);
});
function serializeResult(result) {
if (!isResult(result)) {
throw new Error("The provided value is not an OperationResult");
}
return {
_isOperationResult: true,
// Extract the tuple without the symbol to avoid recursive serialization
result: [result?.[0], result?.[1]]
};
}
__name(serializeResult, "serializeResult");
function deserializeResult(obj) {
if (!obj._isOperationResult) {
throw new Error("The provided object is not a serialized OperationResult");
}
if (!Array.isArray(obj.result) || obj.result.length !== 2) {
throw new Error("The provided object is not a serialized OperationResult");
}
const [error, data] = obj.result;
if (error) return failure(error);
return success(data);
}
__name(deserializeResult, "deserializeResult");
export {
newId,
lifeError,
isLifeError,
lifeErrorToObject,
lifeErrorFromObject,
obfuscateLifeError,
isResult,
success,
failure,
attempt,
dataOrThrow,
toPublic,
resultSchema,
serializeResult,
deserializeResult
};
//# sourceMappingURL=chunk-ZHBK6UTM.mjs.map